I'm trying to write a constructor function which takes a generic value implementing some trait by argument, and then boxes it (the point is to then initialize something with these boxes, but the following example is simplified):
struct Struct {}
trait Trait {}
impl Trait for Struct {}
fn f(arg: impl Trait) -> Box<dyn Trait> {
Box::new(arg)
}
fn main() {
let x = Struct {};
f(x);
}
Here, the compiler whines that arg
might not live long enough. This makes perfect sense to me, because the only requirement on arg
is impl Trait
, meaning it might in fact be a reference which implement the trait, in which case it cannot be boxed safely.
What confuses me is the following solution, adding a 'static
bound:
fn f(arg: impl Trait + 'static) -> Box<dyn Trait> {
Box::new(arg)
}
Now, of course it works, but in principle we are now constrained to passing in values with a 'static
lifetime. Yet, the compiler lets me pass in x
without a problem.
Here are my questions, more specifically:
- Doesn't
x
have a lifetime, residing on the stack? Why is it possible to pass it tof
whenarg
has a'static
lifetime bound? Do these bounds only concern the lifetime of references? - Is this solution valid in general, or will I face cases where the compiler will refuse my stack-allocated arguments?
- Is there a nicer way of saying "any type that impls
Trait
where the type is not a reference?
Note that 1) is mostly answered by Why does Rust require a `'static` lifetime for this variable? but I am left confused as to if this is the idiomatic way of boxing arguments.
EDIT:
Now that I understand better, I am left wondering what is the idiomatic solution to fix the compiler error in the case of populating a struct:
struct OtherStruct {
x: Box<dyn Trait>,
}
impl OtherStruct {
fn new(arg: impl Trait) -> Self { // Does not compile
Self { x: Box::new(arg) }
}
}
The only solutions I see so far are 1) adding a lifetime parameter to OtherStruct
(not so great), adding a 'static
lifetime bound to arg
(I'm not sure if this is OK?)
&'static arg
vs.arg: 'static
are not the same. The first is a lifetime required of a reference, and the second is a bound. In the second case,arg
thus bounded doesn't itself have to be static, it just has to either own all its data, or if its struct has references, they have to have a 'static lifetime; butarg
itself doesn't need to be static. – Boppimpl Trait for &Struct {}
.. then that would permit you to pass a reference toTrait
objects to functionf()
like so:f(&x)
. In this specific case,x
will have to have a static lifetime . This probably confuses the issue, but worth mentioning anyway. – Bopp'static
constraint don't impede them. I guess what was confusing me is that the bound describes the lifetime of a value after it is moved into the function, while for some reason I expected it to describe the lifetime it has before it. – Shut