不同地方不同物体的寿命相等

时间:2016-07-05 08:39:02

标签: rust

我有一个结构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))
}

这里的问题是为什么它有效?我在想什么:

  'aself

Provider生命周期与client: &hyper::Client的生命周期不同,因为这些对象位于不同的位置:MyProvider位于堆栈中client是我使用的方法结构的一个字段。

所以我认为编译器会成功编译它,但它可能会导致运行时错误或崩溃。我错了吗?

从我的角度来看,“正确”的解决方案是:

fn request<'a, 'b>(&'a self, url: &str, client: &'b hyper::Client) -> hyper::client::RequestBuilder {

1 个答案:

答案 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建议你给selfclient相同的生命周期(即'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>;
//                                        ^^                                                 ^^