使用具有显式生命周期的特征时,无法推断借用表达式的生存期

时间:2014-12-23 20:05:00

标签: rust lifetime

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

但是,如果我添加了命名生命周期参数,那么在调用buf后我无法借用test字段,如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

我对此的理解是,通过将命名生命周期'a添加到&'a mut self参数,BufReader所引用的引用只有self引用的生命周期是有效的,直到函数结束。这与在{。}之后的self.buf的可变借用相冲突。

但是,我不确定为什么我需要self上的命名生命周期参数。在我看来,BufReader引用应该只能在t.test方法调用的生命周期中存在。编译器是否在抱怨,因为self.buf借用必须确保只有&self借用才能生存?如何在仍然只在方法调用的生命周期中借用它的时候我会怎么做呢?

任何帮助解决这个问题和理解更多关于语义的信息都会非常感激!

更新

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

我可以在问题中看到rustc输出,它试图指出错误是什么,但是我无法理解它究竟要说的是什么。

2 个答案:

答案 0 :(得分:2)

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

我不确定你到底要做什么,但是这个编译(在每晚0.13.0每晚生成(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));
    }
}

答案 1 :(得分:1)

修改

我打算在这里复制我的评论:

我原本以为在trait / struct / enum中添加一个生命周期或泛型参数是将它放在特征中的每个方法上的简写,但我错误。我目前的理解是,当该项需要参与生命周期时,您可以为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无法确保您的安全,这是值得认真对待的事情。

但是,我打赌您不需要存储参考。将<'a, T: Bar<'a>>impl移至fn,您就可以开始了。

另一种说法:我敢打赌如果你的特质或结构没有需要,你就不应该impl<A>。将泛型放在方法上。

<强>原始

这可以编译,但我不能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块本身不属于它们   特征或类型定义。参数化更常见   需要它的各种方法。

也许其他评论/答案可以帮助进一步详细解释。