genericsrustlifetimeborrow-checkerborrowing

"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

My instinct tells me this fails because the lifetime of the borrow in the Encoder returned by child() has the same lifetime as the "parent" - but I don't see a way to decouple the lifetimes, even though the returned value should clearly have a smaller-or-equal lifetime than the parent, since it can be dropped beforehand.

I did try a version without the EncoderOutput first as well, just having a &mut O directly in Encoder, but the problem was the same.

My line of thought why this should work: When the scope in main() ends, enc2 is dropped, and the implicit Drop impl for it runs, which cleans up the EncoderOutput::Borrowed and the reference within, thus leaving no other &mut ref to enc1, and enc1 can be mutably borrowed again.

Where am I going wrong with this?


Solution

  • Change:

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

    To:

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

    Fixed compiling example:

    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


    Explanation

    &'a self and &'a mut self are the most common lifetime traps in all of Rust that most beginners and even intermediate-level Rustaceans eventually fall into. I knew that line in your example was wrong the second I saw it, without even trying to understand anything else about the rest of your code. Over 99.9% of the time &'a self and &'a mut self are wrong and it should raise a big red flag whenever you see either, and when you do see them you should aggressively refactor them. Okay, so with that out of the way, here's why they are so bad:

    If you have some container which contains a reference, let's call it Container<'a>, then what is the lifetime of the container? It's the same lifetime as its reference, as the container cannot outlive what it is containing. Let's call that lifetime 'a. Very important: the 'a in Container<'a> represents the entire lifetime of the container. So when you write a method with a &'a self or &'a mut self receiver what you are communicating to the compiler is "In order to call this method, the method must borrow the container for the remainder of its entire lifetime." Now when is this something that you actually want? Virtually never! It's extremely rare that you need to write a method that can only ever be called exactly once because it permanently borrows self for the rest of self's lifetime. Ergo, &'a self and &'a mut self are newbie traps, avoid them.

    Clarification

    &'a self and &'a mut self are only red flags when 'a represents the entire lifetime of self itself. In scenarios like below, where the 'a is scoped just to a single method, then it's fine:

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