为什么这个借用仍然“活跃”?

这是我遇到的事情的简化示例:

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即使在退出它被借用的块之后仍然被认为是被借用的,使用借用的所有东西也被删除了?

回答

数据可能会流向另一个地方。
要理解为什么编译器认为不可变借用可能仍然存在是正确的,请考虑以下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执行存在,但将呼吁禁止任何人foobarBad

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>`
}

如果不是这种情况,您可能需要提供更多实现细节而不是导致诊断错误的示例。


回答

通常,当您不指定自己的生命周期时,变量的不可变借用的生命周期会在变量超出范围时结束。但是在您的代码中,您指定'b了不可变借用的显式生命周期。

对于借用检查器,唯一知道的关于生命周期的'b是它是'a(由于where 'a: 'b)的子集。特别是,借用检查器不会假设不可变借用的范围结束于},因为没有指定关于 的任何类型'b

编译器实际上是大声说出来的:

// 类型注解需要self.foo借用'b

概括

为什么即使在退出它被借用的块之后 self.foo 仍然被认为是被借用的,使用借用的所有东西也被删除了?

因为您指定的生命周期不会在块结束时结束。借用检查器不关心与借用相关的一些变量超出范围。

  • I am saying the same thing you are saying. What part of my answer makes you think I am saying something else?

以上是为什么这个借用仍然“活跃”?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>