这是我遇到的事情的简化示例:
trait Bla<'a> {
fn create(a: &'a Foo) -> Self;
fn consume(self) -> u8;
}
struct Foo;
impl Foo {
fn take(&mut self, u: u8) {}
}
struct Bar {
foo: Foo,
}
impl Bar {
fn foobar<'a, 'b, B: Bla<'b>>(&'a mut self)
where 'a: 'b {
let u = {
// immutable borrow occurs here
// type annotation requires that `self.foo` is borrowed for `'b`
let foo: &'b Foo = &self.foo;
let b = B::create(foo);
b.consume()
};
// error[E0502]: cannot borrow `self.foo` as mutable because it is also borrowed as immutable
self.foo.take(u);
}
}
为什么 self.foo
即使在退出它被借用的块之后仍然被认为是被借用的,使用借用的所有东西也被删除了?
答案 0 :(得分:10)
数据可能流动到另一个地方。
要理解为什么编译器认为不可变借用 可能 仍然存在是正确的,请考虑以下有效的 Bla
实现:
#![feature(once_cell)]
// The data stored in this global static variable exists for the entirety of runtime.
static REFS: SyncLazy<Mutex<Vec<&'static Foo>>> = SyncLazy::new(|| Mutex::new(vec![]));
struct Bad;
impl Bla<'static> for Bad {
fn create(a: &'static Foo) -> Self {
// The reference can go somewhere other than `Self`!
REFS.lock().unwrap().push(a);
Bad
}
fn consume(self) -> u8 {
// Even if `self` is consumed here,
// `self` doesn't necessarily destory the immutable borrow.
0
}
}
当您的通用 foobar
函数接受 Bla
的任何有效实现时,有效的实现可能会使用 &'static
借用,以保证借用持续整个运行时。因此,它是一个错误,因为 consume
不能保证不可变借用消失。
您可能正在寻找 for<>
语法,因为它允许 Bad
实现存在但不允许任何人使用 foobar
调用 Bad
:
impl Bar {
fn foobar<B: for<'b> Bla<'b>>(&mut self) {
let u = {
let foo: &Foo = &self.foo;
let b = B::create(foo);
b.consume()
};
self.foo.take(u);
}
}
fn main() {
Bar::foobar::<Bad>(&mut Bar { foo: Foo })
// ^ error: implementation of `Bla` is not general enough
// | `Bad` must implement `Bla<'0>`, for any lifetime `'0`...
// | ...but it actually implements `Bla<'static>`
}
如果不是这种情况,您可能需要提供更多实现细节,而不是导致诊断错误的示例。
答案 1 :(得分:6)
通常,当您不指定自己的生命周期时,变量的不可变借用的生命周期会在变量超出范围时结束。但是在您的代码中,您指定了不可变借用的显式生命周期 'b
。
对于借用检查器,唯一知道的关于生命周期 'b
是它是 'a
的子集(由于 where 'a: 'b
)。特别是,借用检查器不会假设不可变借用的范围在 }
结束,因为没有指定关于 'b
的那种类型。
编译器实际上是大声说出来的:
<块引用>// 类型注解要求 self.foo
被借用为 'b
为什么即使在退出它被借用的块之后,self.foo 仍然被认为是被借用的,使用借用的所有东西也被删除了?
因为您指定的生命周期不会在块结束时结束。借用检查器不关心与借用相关的一些变量超出范围。