我有一个结构UrlShortener
:
pub struct UrlShortener {
client: hyper::Client,
}
impl UrlShortener {
pub fn new() -> UrlShortener {
UrlShortener {
client: hyper::Client::new(),
}
}
pub fn get(&self, url: &str) -> Result<String, Error> {
let mut response = MyProvider.request(url, &self.client).send().unwrap();
/// ...
}
}
MyProvider
看起来像这样:
pub trait Provider {
fn name(&self) -> &str;
fn request(&self, url: &str, client: &hyper::Client) -> hyper::client::RequestBuilder;
}
pub struct MyProvider;
impl Provider for MyProvider {
fn name(&self) -> &str {
"myprovider"
}
fn request(&self, url: &str, client: &hyper::Client) -> hyper::client::RequestBuilder {
client.get(&format!("http://example.com/create.php?format=simple&url={}", url))
}
}
我试图在第一次编译它时不起作用:
src/lib.rs:21:16: 21:19 error: cannot infer an appropriate lifetime for autoref due to conflicting requirements [E0495]
src/lib.rs:21 client.get(&format!("http://example.com/create.php?format=simple&url={}", url))
^~~
src/lib.rs:20:5: 22:6 help: consider using an explicit lifetime parameter as shown: fn request<'a>(&'a self, url: &str, client: &'a hyper::Client)
-> hyper::client::RequestBuilder
src/lib.rs:20 fn request(&self, url: &str, client: &hyper::Client) -> hyper::client::RequestBuilder {
src/lib.rs:21 client.get(&format!("http://example.com/create.php?format=simple&url={}", url))
src/lib.rs:22 }
error: aborting due to previous error
error: Could not compile `urlshortener`.
我根据编译器的建议改变了它,它运行正常。
fn request<'a>(&'a self, url: &str, client: &'a hyper::Client) -> hyper::client::RequestBuilder {
client.get(&format!("http://example.com/create.php?format=simple&url={}", url))
}
这里的问题是为什么它有效?我在想什么:
'a
中self
的
Provider
生命周期与client: &hyper::Client
的生命周期不同,因为这些对象位于不同的位置:MyProvider
位于堆栈中client
是我使用的方法结构的一个字段。
所以我认为编译器会成功编译它,但它可能会导致运行时错误或崩溃。我错了吗?
从我的角度来看,“正确”的解决方案是:fn request<'a, 'b>(&'a self, url: &str, client: &'b hyper::Client) -> hyper::client::RequestBuilder {
答案 0 :(得分:5)
所以我认为编译器会成功编译它,但它可能会导致运行时错误或崩溃。
除非您使用某些unsafe {}
代码,否则在Rust中不会发生这种情况。 Rust总是静态地检查边界,无论变量是在堆栈还是堆上,还是字段或其他内容都无关紧要。
至于Rust的建议:由于RequestBuilder
本身有一个生命周期,因此具有以下功能:
fn request(&self, url: &str, client: &hyper::Client) -> hyper::client::RequestBuilder;
相当于:
fn request<'a, 'b, 'c>(&'a self, url: &'b str, client: &'c hyper::Client) -> hyper::client::RequestBuilder<'a>;
// ^^ ^^
因为elisions rules。请注意该示例的重要规则:
如果有多个输入生命周期,但其中一个是
&self
或&mut self
,则自我的生命周期将分配给所有省略的输出生命周期。
那时Rust会给你一个误导性的建议。在您的函数中,返回值取决于client
,但实际上不取决于self
。 Rust建议你给self
和client
相同的生命周期(即'a == 'c
):
fn request<'a, 'b>(&'a self, url: &'b str, client: &'a hyper::Client) -> hyper::client::RequestBuilder<'a>;
// ^^ ^^ ^^
但是这样就足够了:
fn request<'a, 'b, 'c>(&'a self, url: &'b str, client: &'c hyper::Client) -> hyper::client::RequestBuilder<'c>;
// ^^ ^^ ^^
可以用elision编写:
fn request<'c>(&self, url: &str, client: &'c hyper::Client) -> hyper::client::RequestBuilder<'c>;
// ^^ ^^