I have some code that's giving me error E0478 (I love these error code references by the way). The example erroneous code in the index is (dyn added by me):
// Check that the explicit lifetime bound (`'SnowWhite`, in this example) must
// outlive all the superbounds from the trait (`'kiss`, in this example).
trait Wedding<'t>: 't { }
struct Prince<'kiss, 'SnowWhite> {
child: Box<dyn Wedding<'kiss> + 'SnowWhite>,
// error: lifetime bound not satisfied
}
And the solution is to bound the lifetime parameters of struct Prince:
trait Wedding<'t>: 't { }
struct Prince<'kiss, 'SnowWhite: 'kiss> { // You say here that 'SnowWhite
// must live longer than 'kiss.
child: Box<dyn Wedding<'kiss> + 'SnowWhite>, // And now it's all good!
}
I believe my particular instance of the error is not due to neglecting this bound, but rather flipping it:
trait Wedding<'t>: 't { }
struct Prince<'kiss: 'SnowWhite, 'SnowWhite> { // This line is different
child: Box<dyn Wedding<'kiss> + 'SnowWhite>,
}
Sure enough, this code gives me E0478.
I was under the impression that my way of writing it represents the correct relationship between lifetimes. In particular:
The thing behind the Wedding trait object has borrowed data with the 'kiss lifetime
The trait object itself has lifetime 'SnowWhite
Therefore, the borrowed data 'kiss must outlive 'SnowWhite. 'kiss: 'SnowWhite
But the compiler says this is not correct. Can anyone explain what I am misunderstanding?
This declaration describes absolutely no relationship between the two lifetime annotations. Said another way, either may be 'long or 'short, or they may be the same. Either may be 'static and the other may be temporary.
However:
Box<dyn Wedding<'kiss> + 'SnowWhite>
This introduces an implied constraint that requires 'SnowWhite to outlive 'kiss. And yet there is no such relationship established between them. The solution is, as you discovered, applying the constraint to the lifetime declarations.
struct Prince<'kiss, 'SnowWhite: 'kiss>
Now the compiler is disallowed from choosing a lifetime for 'SnowWhite that is shorter than 'kiss, where the unconstrained version is allowed to choose anything for either.
edit:
I think the second bullet point is a misunderstanding. dyn T + 'a doesn't mean the trait object's lifetime is 'a. It's a constraint meaning that the concrete type that implements the trait can only contain lifetimes as long as 'a. This is probably this misconception (or maybe number 6 on that list).
There's nothing intrinsically about borrowing here:
trait Wedding<'t>: 't { }
This is a trait with a lifetime parameter, which can be used in the definition of the trait (though you haven't done so in the provided code). Plus a bound on Self requiring that it meets a : 't bound. That effectively means, all lifetimes it contains meets a : 't bound -- are 't or longer.
Incidentally, did you add the bound for any particular reason? There's a good chance you don't need it.
When in type-erased dyn form:
child: Box<dyn Wedding<'kiss> + 'SnowWhite>
The dyn lifetime 'SnowWhite has an upper limit of the longest bound the erased type can meet, and -- due to the bound on Self -- a lower limit of 'kiss. It represents some duration where erased type is valid. The trait object itself is only valid for 't, because that lifetime is also part of its type.
The dyn lifetime is covariant whereas the trait parameter is invariant. So a Prince<'kiss, 'SnowWhite> can always coerce to a Prince<'kiss, 'kiss>.
Do you need the lifetimes to be distinct for any particular reason? The main use case where one cares about the dyn lifetime is needing it to be 'static.
I honestly don't know why 'SnowWhite: 'kiss isn't an inferred bound for Prince in the example, like it would be for a nested reference, say.