I'd prefer not to have callers to add_thing be responsible for creating the Box, but I can't find a way to do it. Here's what I've tried:
impl Container {
pub fn add_thing(mut self, thing: impl Thing) -> Self {
self.things.push(Box::new(thing));
// ^^ error[E0310]: the parameter type `impl Thing` may not live long enough
self
}
}
Is there a way to take ownership of something that implements a trait? thing: dyn Trait also fails with something about struct size not being known at compile time.
Trait objects have to have an explicit associated lifetime, since their concrete underlying type is not known to the compiler at the use site. Therefore, a dyn Trait is really a dyn Trait + 'lifetime.
Box<dyn Trait> is actually shorthand for Box<dyn Trait + 'static>. If your trait object is an owning type, you can just slap a 'static bound on the concrete input type:
OK, I saw that, but my previous (maybe incorrect?) understanding was that 'static implies the underlying struct is a compile-time static. What I think I hear you saying is that 'static just means ownership and can be properly dropped when Container is dropped. Is that correct?
(If so, that's exactly what I'm looking for in this context.)
This is essentially correct. As a bound, :'static means that the value is not restricted to any particular stack frame— it can be kept for as much or as little time as the container needs.
As a reference lifetime (&'static T), it means that the target T will not be dropped before the end of the program.
One way to illustrate this is that + 'expiry_date represents (a lower-bound on) some inherent expiry date that your canned food / (the type of) your value has.
As you can see, it's kind of separate question to that of when do you eat or throw away that canned food / of how long you keep hold of that value.
'static represents an "end of times" / "never ending" date (I personally like to call it 'forever)
(the name static in it is kind of a misnomer related to the fact that static storage, indeed, never goes stale, so references to it (&'static) get to be usable forever. But, as a counter-example, if you Box::leak() some heap-allocated integer, for instance, then you end up with something that never expires as well, and which is completely unrelated to static storage)
So, if something is : 'static, it means it has no inherent expiry date.
In most programming languages, everything is : 'static; in the case of Rust, this would relate to what people's first intuition of an "owned"[1] thing is.
The main counter-example is indeed when you have (own) something where part of its data happens to be borrowing / referencing some 'temporary data. In that case, it could technically be problematic to use such a thing beyond the end of this 'temporary duration. But for any 'time ending at most as late as 'time does (i.e., for any 'time where 'temporary : 'time), which includes 'time = 'temporary, it would be fine to be using that thing:
the : 'time property would hold for that thing.
Another way to look at it, is to replace occurrences of + 'duration with + UsableFor<'duration>.
It shouldn't be that suprising to then say that a String is : UsableFor<'forever>,
whereas a &'short str reference is "only" : UsableFor<'short>
In your case, your "owned" dyn traits were thus Box<dyn Thing + UsableFor<'forever>>, and, when writing impl Thing, you needed to add the + UsableFor<'forever> requirement for that thing, i.e., + 'static.
Trick
When you expect all implementations of your trait "to be owned" / not to be lifetime-infected / to be : UsableFor<'forever>, you can then directly write it as a super-bound of the trait definition:
trait Thing : 'static {
or, using my own educational definition:
trait UsableFor<'duration> : 'duration {}
impl<'duration, T : 'duration> UsableFor<'duration> for T {}
the definition of Thing could then be:
trait Thing : UsableFor<'static> {}
That way an impl Thing will then be understood to be an impl 'static + Thing, thereby removing the need to annotate that
In practice, especially with dyn Traits, the actual meaning of ownership is not about being : 'static / about the lack of lifetime-parameters / about the lack of being lifetime-infected, but rather, about who is responsible for dropping something / move something. For instance, a Box<dyn 'short + Display> can be deemed owned for it represents the ownership over that Box, and yet it is not : 'static. For instance, it could have originated from a Box<&'short str>, that is, an owned Box containing a borrow to a str. ↩︎
It is not. 'static means "doesn't contain references valid shorter than 'static", which in practice mostly implies "does not contain references".
You are confusing the &'static … lifetime annotation of references and the 'static lifetime bound on a type:
&'static T means that "this reference is valid for at least the static lifetime". This implies that it must point to something that is actually alive forever, e.g. a compile-time constant or a real static item.
T: 'static means that "values of this type are allowed to be kept around forever". It doesn't mean that values of this type necessarily live forever.
That latter (incorrect) interpretation wouldn't even make any sense as a type-level constraint, if you think about it! Should the following program be invalid?
const COMPILE_TIME: u64 = 1337;
fn main() {
{
let x = COMPILE_TIME;
}
// `x` is clearly dropped here, its value didn't live forever!
}
If 'static implied "all values of this type live forever", then the above program would have to be rejected – clearly, for no sensible reason.
'static means "doesn't contain references valid shorter than 'static", which in practice mostly implies "does not contain references".
Ah, that is the clarification that I needed. Perfectly suited for this situation and the solution you described earlier compiles and behaves as intended. Thank you!