【问题标题】:Unable to infer lifetime for borrow expression when using a trait with an explicit lifetime使用具有显式生命周期的特征时,无法推断借用表达式的生命周期
【发布时间】:2015-02-22 00:03:13
【问题描述】:
use std::io::BufReader;
struct Foo {
    buf: [u8, ..10]
}

trait Bar<'a> {
    fn test(&self, arg: BufReader<'a>) {}
}

impl<'a, T: Bar<'a>> Foo {
    fn bar(&'a mut self, t: T) {
        t.test(BufReader::new(&self.buf));
        let b = &mut self.buf;
    }

    fn baz(&self, t: T) {
        t.test(BufReader::new(&self.buf));
    }
}

fn main() {}

上面的代码编译失败,报错:

lifetimes.rs:17:31: 17:40 error: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
lifetimes.rs:17         t.test(BufReader::new(&self.buf));
                                              ^~~~~~~~~
lifetimes.rs:16:5: 18:6 help: consider using an explicit lifetime parameter as shown: fn baz(&'a self, t: T)
lifetimes.rs:16     fn baz(&self, t: T) {
lifetimes.rs:17         t.test(BufReader::new(&self.buf));
lifetimes.rs:18     }
error: aborting due to previous error

但是,如果我添加命名的生命周期参数,则在调用test 后,我不能可变借用buf 字段,如fn bar 所示。注释掉fn baz 并尝试编译结果:

lifetimes.rs:13:22: 13:30 error: cannot borrow `self.buf` as mutable because it is also borrowed as immutable
lifetimes.rs:13         let b = &mut self.buf;
                                     ^~~~~~~~
lifetimes.rs:12:32: 12:40 note: previous borrow of `self.buf` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `self.buf` until the borrow ends
lifetimes.rs:12         t.test(BufReader::new(&self.buf));
                                               ^~~~~~~~
lifetimes.rs:14:6: 14:6 note: previous borrow ends here
lifetimes.rs:11     fn bar(&'a mut self, t: T) {
lifetimes.rs:12         t.test(BufReader::new(&self.buf));
lifetimes.rs:13         let b = &mut self.buf;
lifetimes.rs:14     }
                    ^
error: aborting due to previous error

我对此的理解是,通过在&amp;'a mut self参数中加上命名生命周期'a,只要self引用有效,BufReader所取的引用就有生命周期,一直到最后的功能。这与后行 self.buf 的可变借用相冲突。

但是,我不确定为什么需要 self 上的命名生命周期参数。在我看来,BufReader 引用应该只能在 t.test 方法调用的生命周期内存在。编译器是否在抱怨,因为必须确保 self.buf 借用只与 &amp;self 借用一样长?在方法调用的整个生命周期内仍然只借用它的情况下,我将如何去做呢?

任何帮助解决这个问题和理解这里的语义将不胜感激!

更新

所以我仍在研究这个问题,我发现this test casethis issue 基本上显示了我正在尝试做的事情。我非常想了解为什么测试用例链接指向的错误是错误。

我可以在问题 rustc 输出中看到试图指出错误是什么,但我无法理解它到底想表达什么。

【问题讨论】:

  • 您能否详细解释一下为什么您想要trait Bar&lt;'a&gt; 以及您的目标是什么?就我而言,我认为这是将&lt;'a&gt; 放在特征中的每个方法上的简写,但我错了。我目前的理解是,当 implementer 需要参与生命周期时(可能是因为它正在存储具有该生命周期的引用),您可以为 trait 添加生命周期。
  • 我想做的是使用bincode 序列化结构/枚举。 Bincode 定义了一个带有生命周期参数的特征,DecoderReader&lt;'a, R&gt;,我正在尝试使用它。问题出现是因为我的结构本身有一个类型参数,因此在我的结构的impl 中,我必须指定它是DecoderReader&lt;'a, R&gt;。这是来自同一项目here 的示例。
  • 上面的例子有效,但是当我尝试在类似上面的函数中对self 进行另一个可变引用时,就会出现问题。它错误地说我必须等待前一个可变借用完成,这是在函数的末尾。
  • “当我尝试使用另一个可变引用时” - 如果我理解你,那就是你的问题:你 simply aren't allowed to have two mutable references to the same thing at the same time。这是一个非常不同的问题/问题。
  • 是的,这就是编译器失败的原因。如果查看我上面的示例代码,特别是fn bar 及其相关的 rustc 错误(第二个),您会看到正在发生的事情。我的想法是&amp;'a self 引用使BufReaderfn bar 的长度内保持活动状态,但我想做的只是在调用test 的时间段内让那个借用保持活动状态。

标签: rust lifetime


【解决方案1】:

编辑

我将在这里复制编辑我的评论:

我最初认为向 trait / struct / enum 添加生命周期或泛型参数是把它放在 trait 中的每个方法上的简写,但我错了。我目前的理解是,当该项目需要参与生命周期时,您向 trait / struct / enum 添加生命周期,可能是因为它存储了具有该生命周期的引用。

struct Keeper<'a> {
    counts: Vec<&'a i32>,
}

impl<'a> Keeper<'a> {
    fn add_one(&mut self, count: &'a i32) {
        if *count > 5 {
            self.counts.push(count);
        }
    }

    fn add_two<'b>(&mut self, count: &'b i32) -> i32 {
        *count + 1
    }
}

fn main() {
    let mut cnt1 = 1;
    let mut cnt2 = 2;
    let mut k = Keeper { counts: Vec::new() };

    k.add_one(&cnt1);
    k.add_two(&cnt2);

    // cnt1 += 1; // Errors: cannot assign to `cnt1` because it is borrowed
    cnt2 += 1; // Just fine

    println!("{}, {}", cnt1, cnt2)
}

在这里,我们为Keeper 添加了生命周期,因为它可能会存储给定的引用。借用检查器必须假设当我们调用 add_one 时引用被永久存储,所以一旦我们调用该方法,我们就不能再改变值。

另一方面,

add_two 创建一个只能应用于该函数调用的新生命周期,因此借用检查器知道一旦函数返回,它就是一个真正的所有者.

结果是,如果您需要存储引用,那么在此级别您无能为力。 Rust 无法确保您的安全,这是它非常重视的事情。

但是,我打赌您不需要存储参考。将&lt;'a, T: Bar&lt;'a&gt;&gt;impl 移动到fn,您就可以开始了。

换一种说法:我敢打赌,如果你的 trait 或 struct 不需要它,你就不应该拥有impl&lt;A&gt;。而是将泛型放在方法上。

原创

这可以编译,但我不能 100% 确定它是否符合您的预期:

impl Foo {
    fn baz<'a, T: Bar<'a>>(&'a self, t: T) {
        t.test(BufReader::new(&self.buf));
    }
}

I fell into this trap myself,所以我会粘贴我被告知的内容:

impl 块中的所有内容都是参数化的。我其实从来没有 看到类型参数添加到 impl 块本身不是一部分 的特征或类型定义。参数化更为常见 需要它的各个方法。

也许其他 cmets / 答案可以帮助更详细地解释。

【讨论】:

  • 这是有道理的,我也和你一样假设过。感谢您的帮助!
【解决方案2】:

删除所有显式生命周期也有效。我发现只有在确定需要生命周期时才添加生命周期(即指定两个生命周期应该在编译器无法知道的给定点相交)。

我不确定你到底想要什么,但这可以编译(在 rustc 0.13.0-nightly (cc19e3380 2014-12-20 20:00:36 +0000) 上)。

use std::io::BufReader;
struct Foo {
    buf: [u8, ..10]
}

trait Bar {
    fn test(&self, arg: BufReader) {}
}

impl<T: Bar> Foo {
    fn bar(&mut self, t: T) {
        t.test(BufReader::new(&self.buf));
        let b = &mut self.buf;
    }

    fn baz(&self, t: T) {
        t.test(BufReader::new(&self.buf));
    }
}

【讨论】:

  • 嗯,很有趣。相交生命周期的用例是什么?该语法看起来像'a: 'b + 'c吗?
  • 不,这意味着两个或多个具有相同生命周期的引用。 IE。当您需要指定特定引用(例如函数参数)的生命周期至少与另一个引用(例如函数返回类型)一样长时。实际上,它们的真实生命周期可能非常不同,但您只是告诉编译器这两个生命周期有某种关系。
  • 虽然我认为这通常是正确的方向,但它并没有真正回答这个问题:“当使用具有显式生命周期的特征时”,因为它删除了显式生命周期!
猜你喜欢
  • 1970-01-01
  • 2022-01-06
  • 2020-01-29
  • 2022-08-22
  • 2020-07-26
  • 1970-01-01
  • 2015-04-22
  • 2019-08-05
  • 1970-01-01
相关资源
最近更新 更多