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?
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");
}
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
}