【问题标题】:Rust lifetime errorRust 生命周期错误
【发布时间】:2017-05-03 09:58:11
【问题描述】:

谁能说出以下代码中的生命周期错误是什么? (从我的实际代码中简化)我自己查看过它,但我不知道出了什么问题或如何解决它。当我尝试添加Cell 时出现问题,但我不确定为什么。

use std::cell::Cell;

struct Bar<'a> {
    bar: &'a str,
}
impl<'a> Bar<'a> {
    fn new(foo: &'a Foo<'a>) -> Bar<'a> { Bar{bar: foo.raw} }
}

pub struct Foo<'a> {
    raw: &'a str,
    cell: Cell<&'a str>,
}
impl<'a> Foo<'a> {
    fn get_bar(&self) -> Bar { Bar::new(&self) }
}

编译错误是

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src/foo.rs:15:32
   |
15 |     fn get_bar(&self) -> Bar { Bar::new(&self) }
   |                                ^^^^^^^^

【问题讨论】:

    标签: rust lifetime


    【解决方案1】:

    一、解决办法:

    use std::cell::Cell;
    
    struct Bar<'a> {
        bar: &'a str,
    }
    impl<'a> Bar<'a> {
        fn new(foo: &Foo<'a>) -> Bar<'a> { Bar{bar: foo.raw} }
    }
    
    pub struct Foo<'a> {
        raw: &'a str,
        cell: Cell<&'a str>,
    }
    impl<'a> Foo<'a> {
        fn get_bar(&self) -> Bar<'a> { Bar::new(&self) }
    }
    

    您的代码中有两个问题。第一个是get_bar,您没有指定返回类型的生命周期。当您没有在签名中指定生命周期时,Rust 不会推断出正确的生命周期,它只是根据简单的规则盲目地填充它们。在这种特定情况下,您得到的实际上是fn get_bar&lt;'b&gt;(&amp;'b self) -&gt; Bar&lt;'b&gt;,这显然是错误的,因为self.raw(这是您真正想要的)的生命周期是'a。请参阅 Lifetime Elision 上的 Rust Book 章节。

    第二个问题是您将参数过度限制为Bar::new&amp;'a Foo&lt;'a&gt; 意味着只要它借用的字符串存在,您就需要借用 Foo。但是在一个类型中的借用必须比所述类型的值长,所以在这种情况下 only 有效的生命周期是 'a 匹配被借用事物的整个生命周期......并且 get_bar 的签名相冲突(你说&amp;self 的寿命不一定和'a 一样长,因为它有自己的生命周期)。长话短说:从Foo借用中删除不必要的'a,只留下&amp;Foo&lt;'a&gt;

    换个说法:get_bar 的问题是你没有写足够的约束,Bar::new 的问题是你写的太多了。

    【讨论】:

    • @Antimony 我不确定,但我认为这与 Cell 具有内部可变性有关,可能会影响生命周期的变化的包含类型。将可变性加入混合中往往会使借用检查器更加严格。我会给出一个更彻底的答案,但这是我自己并不扎实的一个方面。
    • 好点。我忘记了方差推断。这可能就是答案。
    【解决方案2】:

    DK 解释了缺少哪些约束以及原因,但我认为我应该在添加 Cell 之前解释为什么代码可以正常工作。原来是因为variance inference

    如果您将推断的生命周期添加到原始代码中并将生命周期变量重命名为唯一的,您会得到

    struct Bar<'b> {
        bar: &'b str,
    }
    impl<'b> Bar<'b> {
        fn new(foo: &'b Foo<'b>) -> Bar<'b> { Bar{bar: foo.raw} }
    }
    
    pub struct Foo<'a> {
        raw: &'a str,
        cell: Cell<&'a str>,
    }
    impl<'a> Foo<'a> {
        fn get_bar<'c>(&'c self) -> Bar<'c> { Bar::new(&self) }
    }
    

    调用Bar::new 时出现问题,因为您将&amp;'c Foo&lt;'a&gt; 传递给期望&amp;'b Foo&lt;'b&gt; 的东西。通常,Rust 中的不可变类型是协变的,这意味着只要 'b 的生命周期比 'a 短,&amp;Foo&lt;'a&gt; 就可以隐式转换为 &amp;Foo&lt;'b&gt;。没有 Cell,&amp;'c Foo&lt;'a&gt; 转换为 &amp;'c Foo&lt;'c&gt;,并通过 'b = 'c 传递给 Bar::new,所以没有问题。

    但是,CellFoo 添加了内部可变性,这意味着协变不再安全。这是因为Bar 可能会尝试将较短生命周期的'b 引用分配回原始Foo,但Foo 要求它持有的所有引用在更长的生命周期'a 中都有效。因此,内部可变性使&amp;Foo 不变,这意味着您不能再隐式地缩短生命周期参数。

    【讨论】:

      猜你喜欢
      • 2014-09-10
      • 2013-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-24
      • 1970-01-01
      • 2013-11-26
      • 2017-12-27
      相关资源
      最近更新 更多