Strange lifetime error when using borrow_mut
use std::borrow::BorrowMut;
trait A {}
struct B;
impl A for B {}
fn func(a: &mut A) {}
fn main() {
let t: Box<A> = Box::new(B);
{
let b = t.borrow_mut();
func(b);
}
}
When compiling above code, it fails with:
error: `t` does not live long enough
--> <anon>:17:1
|
14 | let b = t.borrow_mut();
| - borrow occurs here
...
17 | }
| ^ `t` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
I think the borrow state should end at L16.
Ais a trait so&mut Ais short for&'a mut (A + 'a), which means that... wait, why do we have that, it means that it can borrow itself.cc @nikomatsakis Is this useful behavior at all? Do we do something with reborrows?
Eduard-Mihai Burtescu at 2017-06-06 14:25:20
So, a couple of things:
First, to make this example work, you only need to change from
t.borrow_mut()to&mut *t(you also need to make itlet mut t). Usingborrow_mut()in this way is definitely not recommended, but you could also make it work by adding alet b: &mut Aannotation.Now, as to why we get the error. As @eddyb points out, this is fallout from the defaulting rules around trait objects. In particular,
&mut Adefaults to&'a mut (A + 'a). There are reasons for this that I'll get to in a bit, but it has some drawbacks too. In any case,Box<A>defaults toBox<A + 'static>. If you were using&mut *tinstead oft.borrow_mut(), then the type ofbwould be known to be&mut (A + 'static), and we have a built-in coercion that converts from&'a mut (A + 'static)to&'a mut (A + 'a).When you use
t.borrow_mut(), without the type annotation, the resulting type is a type inference variable, and so the coercion doesn't wind up kicking in. When you use the type annotation, the coercion kicks in.The reason that the trait object lifetime rules are the way they are is that they apply everywhere, not just in function signatures, and in those other cases we can't supply a fresh lifetime -- it's important that the lifetimes apply everywhere, because otherwise we have the same type (i.e., same characters) in two places with incompatible meanings. In general, then, we say that if the trait object appears inside a reference, we will use the lifetime of that reference (even if the reference is
&mut). This means that&Traitor&mut Traitallows the type implementing the trait to have references in it.(Well, in function bodies, I Think we always use inference variables, that's the last part of the puzzle.)
In any case, i'm closing this as expected behavior -- I'm not sure that there's anything else we can do here.
Niko Matsakis at 2017-06-07 21:52:59
(Maybe a better error message? But this case (using
borrow_mut()this way) doesn't feel very representative, so I'm not just I'd want to do it for this particular example -- maybe we can find another one that fails in a similar way?)Niko Matsakis at 2017-06-07 21:53:47
My theory is that the lifetime variable in
Box<A>has to outlive the binding it's a type of, which later extends the lifetime variable&'a mut (A + 'a)to be longer.Eduard-Mihai Burtescu at 2017-06-07 22:00:24
Mm so I take it back. It occurs to me that we could plausibly override the object lifetime default in fn arg position, just as we override it in the body of a fn, to get a fresh lifetime variable -- specifically in the case where you have
&Traitor&mut Trait. This would require some lang-team discussion, probably an (amendment) RFC would be best. But if it's truly backwards compatible it seems like a win-win.Niko Matsakis at 2017-06-07 22:31:07
My theory is that the lifetime variable in Box<A> has to outlive the binding it's a type of, which later extends the lifetime variable &'a mut (A + 'a) to be longer.
Yes, you're right of course -- but the rest of my comment remains accurate, just not the part about
Box<A>beingBox<A+'static>.Niko Matsakis at 2017-06-08 00:01:00
Triage: no change
Maayan Hanin at 2022-03-21 17:46:34