I have this minimal example code:
use std::borrow::BorrowMut;
trait Foo {}
struct Bar;
impl Foo for Bar {}
fn main() {
let mut encryptor: Box<Foo> = Box::new(Bar);
encrypt(encryptor.borrow_mut());
}
fn encrypt(encryptor: &mut Foo) { }
but it fails with this error:
error: `encryptor` does not live long enough
--> src/main.rs:11:1
|
10 | encrypt(encryptor.borrow_mut());
| --------- borrow occurs here
11 | }
| ^ `encryptor` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
The kind people at #rustbeginners found that I have to dereference the box to get the contents, and then borrow the contents. Like this:
trait Foo {}
struct Bar;
impl Foo for Bar {}
fn main() {
let mut encryptor: Box<Foo> = Box::new(Bar);
encrypt(&mut *encryptor);
}
fn encrypt(encryptor: &mut Foo) { }
It works, but I don't understand it.
Why do I need to dereference first? What is the error trying to say? Normally it isn't an error that a value is dropped at the end of the function.
Apparently it's not just me who doesn't understand how this works; an issue has been filed.
Let's start with a change that allows the code to work:
fn encrypt(encryptor: &mut (Foo + 'static)) { }
The important difference is the addition of + 'static
to the trait object - the parens are just needed for precedence.
The important thing to recognize is that there are two lifetimes present in &Foo
:
&'a Foo
&(Foo + 'b)
. If I'm reading the RFCs correctly, this was introduced by RFC 192, and RFC 599 specified reasonable defaults for the lifetimes. In this case, the lifetimes should expand like:
fn encrypt(encryptor: &mut Foo) { }
fn encrypt<'a>(encryptor: &'a mut (Foo + 'a)) { }
On the other end of the pipe, we have a Box<Foo>
. Expanded by the rules of the RFC, this becomes Box<Foo + 'static>
. When we take a borrow of it, and try to pass it to the function, we have an equation to solve:
'static
.'static
. Uh oh!The Box
will be dropped at the end of the block so it certainly isn't static.
The fix with explicit lifetimes allows the lifetime of the reference to the trait object to differ from the lifetime of the references inside the trait object.
If you needed to support a trait object with internal references, an alternate is to do something like:
fn encrypt<'a>(encryptor: &mut (Foo + 'a)) { }
True credit for this explanation goes to nikomatsakis and his comment on GitHub, I just expanded it a bit.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With