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
但是,如果我添加命名的 lifetime 参数,则在调用 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
我对此的理解是,通过将命名的生命周期'a
添加到 &'a mut self
参数中,只要self
引用有效,BufReader
获取的引用就具有生存期,直到函数结束。这与之后的行上self.buf
的可变借用冲突。
但是,我不确定为什么我需要在self
上使用命名的生命周期参数。在我看来,BufReader
引用应该只能在t.test
方法调用的生命周期内存在。编译器是否抱怨,因为必须确保self.buf
借用只存在于&self
借用的时间?我将如何做到这一点,同时仍然只在方法调用的生存期内借用它?
有助于解决此问题并了解有关此处语义的更多信息的帮助将不胜感激!
更新
所以我仍在研究这个问题,我发现这个测试用例和这个问题基本上显示了我正在尝试做的事情。我非常想了解为什么测试用例链接指出的错误是错误。
我可以在问题 rustc 输出中看到试图指出错误是什么,但我很难理解它到底想说什么。
删除所有显式生存期也有效。我发现我只在确定需要它们时才添加生命周期(即指定两个生命周期应该在编译器无法知道的给定点相交)。
我不确定你要做什么,但这可以编译(在 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));
}
}
Edit
我将在这里复制编辑我的评论:
我最初认为在 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 无法确保你的安全,这是它认真对待的事情。
但是,我敢打赌您不需要存储引用。将<'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));
}
}
我自己掉进了这个陷阱,所以我会粘贴我被告知的内容:
impl 块中的所有内容都已参数化。我其实从来没有 看到添加到 impl 块本身的类型参数不是部分 特征或类型定义。参数化更为常见 需要它的各个方法。
也许其他评论/答案可以帮助更详细地解释。