【问题标题】:Equal lifetimes for different objects in different places不同地方的不同对象的生命周期相同
【发布时间】:2016-07-05 08:39:01
【问题描述】:

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

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

Providerself'a 生命周期与client: &amp;hyper::Client 的生命周期不同,因为这些对象位于不同的位置:MyProvider 在堆栈上,client 是结构的字段我使用的方法。

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

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

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

【问题讨论】:

    标签: rust


    【解决方案1】:

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

    除非您使用一些 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。请注意该示例的重要规则:

    如果有多个输入生命周期,但其中之一是 &amp;self&amp;mut self,则将 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>;
    //                      ^^                              ^^                                                 ^^
    

    可以用省略写成:

    fn request<'c>(&self, url: &str, client: &'c hyper::Client) -> hyper::client::RequestBuilder<'c>;
    //                                        ^^                                                 ^^
    

    【讨论】:

      猜你喜欢
      • 2020-02-26
      • 1970-01-01
      • 2018-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-05
      • 1970-01-01
      相关资源
      最近更新 更多