"cannot borrow as mutable more than once" 即使在放弃第一次借用之后

"cannot borrow as mutable more than once" even after dropping the first borrow

trait Output {
    fn write(&mut self, text: &str);
}

struct DummyOutput {}

impl Output for DummyOutput {
    fn write(&mut self, text: &str) {
        // self does not need to be mut in this reproducer,
        // but it would in the real version
        println!("{}", text);
    }
}

enum EncoderOutput<'a, T> {
    Owned(T),
    Borrowed(&'a mut T),
}

impl<'a, T> AsMut<T> for EncoderOutput<'a, T> {
    fn as_mut(&mut self) -> &mut T {
        match self {
            EncoderOutput::Owned(ref mut o) => o,
            EncoderOutput::Borrowed(b) => b,
        }
    }
}

struct Encoder<'a, O: Output> {
    output: EncoderOutput<'a, O>,
}

impl<'a, O: Output> Encoder<'a, O> {
    // here's the idea:
    // every child instance will have a borrowed output,
    // and always only one level of indirection, i.e.:
    // - root: "&mut O" or "O"
    // - child1: "&mut O"
    // - child2: "&mut O"
    // but never:
    // - childN: "&mut &mut O"
    fn child(&'a mut self) -> Self {
        Encoder {
            output: EncoderOutput::Borrowed(self.output.as_mut()),
        }
    }
}

fn main() {
    let mut enc1 = Encoder {
        output: EncoderOutput::Owned(DummyOutput {}),
    };

    {
        // I know this borrows mutably from enc1
        let mut enc2 = enc1.child();

        // so this will obviously not work:
        // enc1.output.as_mut().write("bar 2b");

        // but this does work:
        enc2.output.as_mut().write("bar 2a");
    } // but then the borrow "enc2" should be dropped here?

    // so why does this fail with:
    // "cannot borrow [...] as mutable more than once"
    enc1.output.as_mut().write("bar 3");
}
error[E0499]: cannot borrow `enc1.output` as mutable more than once at a time
  --> src/main.rs:68:5
   |
57 |         let mut enc2 = enc1.child();
   |                        ---- first mutable borrow occurs here
...
68 |     enc1.output.as_mut().write("bar 3");
   |     ^^^^^^^^^^^
   |     |
   |     second mutable borrow occurs here
   |     first borrow later used here

我的直觉告诉我这失败了,因为 child() 返回的 Encoder 中借用的生命周期与“父”的生命周期相同 - 但我没有找到办法解耦生命周期,即使返回值的生命周期明显小于或等于父值,因为它可以预先删除。

我也先尝试了一个没有 EncoderOutput 的版本,只是在 Encoder 中直接有一个 &mut O,但问题是一样的。

我的想法为什么这应该有效:当 main() 中的作用域结束时,enc2 被删除,并且它的隐式 Drop impl 运行,它清理了EncoderOutput::Borrowed 和其中的引用,因此不会留下 enc1 的其他 &mut 引用,并且 enc1 可以再次可变地借用。

我哪里出错了?

经过一番折腾,解决方案是将child改为:

    fn child(&mut self) -> Encoder<'_, O> {
        Encoder {
            output: EncoderOutput::Borrowed(self.output.as_mut()),
        }
    }

现在我知道问题出在一个方法的生命周期上,因此 rustc 推断主题(self)必须保持借用的时间不超过和超过它的寿命,所以一旦这个方法被调用你' d被“锁定”。遗憾的是,我不记得确切的问题或找到它的先前实例,尽管我知道它们存在。

因此,虽然我能想到可能的解释,但因为我不知道它们是否正确,所以我不会提供任何解释,抱歉。但基本上通过使用 '_ 你告诉 rustc 给你一个新的输出生命周期并找出界限。

变化:

fn child(&'a mut self) -> Encoder<'a, O>;

收件人:

fn child(&mut self) -> Encoder<'_, O>;

修复编译示例:

trait Output {
    fn write(&mut self, text: &str);
}

struct DummyOutput {}

impl Output for DummyOutput {
    fn write(&mut self, text: &str) {
        println!("{}", text);
    }
}

enum EncoderOutput<'a, T> {
    Owned(T),
    Borrowed(&'a mut T),
}

impl<'a, T> AsMut<T> for EncoderOutput<'a, T> {
    fn as_mut(&mut self) -> &mut T {
        match self {
            EncoderOutput::Owned(ref mut o) => o,
            EncoderOutput::Borrowed(b) => b,
        }
    }
}

struct Encoder<'a, O: Output> {
    output: EncoderOutput<'a, O>,
}

impl<'a, O: Output> Encoder<'a, O> {
    // line below changed from:
    // fn child(&'a mut self) -> Encoder<'a, O> {
    // to:
    // child(&mut self) -> Encoder<'_, O> {
    fn child(&mut self) -> Encoder<'_, O> {
        Encoder {
            output: EncoderOutput::Borrowed(self.output.as_mut()),
        }
    }
}

fn main() {
    let mut enc1 = Encoder {
        output: EncoderOutput::Owned(DummyOutput {}),
    };

    {
        let mut enc2 = enc1.child();
        enc2.output.as_mut().write("bar 2a");
    }

    enc1.output.as_mut().write("bar 3");
}

playground


说明

&'a self&'a mut self 是所有 Rust 中最常见的生命周期陷阱,大多数初学者甚至中级 Rustaceans 最终都会陷入其中。我一看到你的例子中的那一行就知道它是错误的,甚至没有试图理解你代码其余部分的任何其他内容。超过 99.9% 的时间 &'a self&'a mut self 是错误的,无论何时你看到它们都应该发出一个大红旗,当你看到它们时,你应该积极地重构它们。好的,撇开这些不谈,这就是它们如此糟糕的原因:

如果你有一些包含引用的容器,我们称之为 Container<'a>,那么容器的生命周期是多少?它与其引用的生命周期相同,因为容器不能比它所包含的内容更长寿。我们称其为生命周期 'a。非常重要:Container<'a>中的'a代表容器的整个生命周期。因此,当您使用 &'a self&'a mut self 接收器编写方法时,您与编译器通信的是 “为了调用此方法,该方法必须借用容器作为剩余部分它的整个生命周期。” 现在,您真正想要的是什么时候?几乎从来没有!极少需要编写只能调用一次的方法,因为它会在 self 的剩余生命周期中永久借用 self。因此,&'a self&'a mut self 是新手陷阱,请避开它​​们。

澄清

'a 代表 self 本身的整个生命周期时,

&'a self&'a mut self 只是危险信号。在像下面这样的场景中,'a 的范围仅限于一个方法,那就没问题了:

// 'a only local to this method, this is okay
fn method<'a>(&'a self) {
    // etc
}