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:
x
have a lifetime, residing on the stack? Why is it possible to pass it to f
when arg
has a 'static
lifetime bound? Do these bounds only concern the lifetime of references?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?)
You have a couple misconceptions here.
Doesn't x have a lifetime, residing on the stack? Why is it possible to pass it to f when arg has a 'static lifetime bound? Do these bounds only concern the lifetime of references?
When you are doing f(x)
, because you don't take a reference to x
, you are moving the value of x
into the function, which shifts its lifetime. If you tried to use x
again after calling f(x)
, Rust will fail to compile your code and tell you this.
As for the + 'static
bound... Box<dyn Trait>
is shorthand for Box<dyn Trait + 'static>
, which is why the compiler was giving you an error. The type system needs to know the lifetime of the implementation inside of the box so that it can check it. You could give the box a different lifetime, if you wish, explicitly:
fn f<'a>(arg: impl Trait + 'a) -> Box<dyn Trait + 'a> {
Box::new(arg)
}