I'm very new to Rust and system languages in general. And I'm currently playing around with Rust to explore the language. I've a problem that I cannot fix by myself. And I think I've understanding problem whats going on.
I wan't to store objects that implements the trait BaseStuff in a vector. In Rust not a simple task for me :-).
Here is my example code that won't compile.
trait BaseStuff {}
struct MyStuff {
value: i32,
}
struct AwesomeStuff {
value: f32,
text: String,
}
impl BaseStuff for MyStuff {}
impl BaseStuff for AwesomeStuff {}
struct Holder {
stuff: Vec<BaseStuff>,
}
impl Holder {
fn register(&mut self, data: impl BaseStuff) {
self.stuff.push(data);
}
}
fn main() {
let my_stuff = MyStuff { value: 100 };
let awesome_stuff = AwesomeStuff {
value: 100.0,
text: String::from("I'm so awesome!"),
};
let mut holder = Holder { stuff: vec![] };
holder.register(my_stuff);
}
error[E0277]: the size for values of type
(dyn BaseStuff + 'static)cannot be known at compilation time --> src\main.rs:17:5 | 17 |
stuff: Vec, // unknown size | ^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time | = help: the traitstd::marker::Sizedis not implemented for(dyn BaseStuff + 'static)= note: to learn more, visit https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait = note: required bystd::vec::Vecerror: aborting due to previous error
For more information about this error, try
rustc --explain E0277. error: Could not compileplayground.The compiler message is clear and I understand the message. I can implement the trait BaseStuff in any struct I want't so its unclear which size it is. Btw the link isn't helpful because its pointing to outdated site...
The size of String is also unknown, but the String implements the trait std::marker::Sized and that's why a Vec<String> will work without problems. Is that correct?
In the rust book I read for data types with unknown size I've to store these data on the heap instead of the stack. I changed my code as follows.
struct Holder {
stuff: Vec<Box<BaseStuff>>,
}
impl Holder {
fn register(&mut self, data: impl BaseStuff) {
self.stuff.push(Box::new(data));
}
}
Now I'm hitting a new compiler issue:
error[E0310]: the parameter type
impl BaseStuffmay not live long enough --> src\main.rs:22:25 | 21 | fn register(&mut self, data: impl BaseStuff) { |
--------------- help: consider adding an explicit lifetime boundimpl BaseStuff: 'static... 22 | self.stuff.push(Box::new(data));
| ^^^^^^^^^^^^^^ | note: ...so that the typeimpl BaseStuffwill meet its required lifetime bounds --> src\main.rs:22:25 | 22 | self.stuff.push(Box::new(data));
| ^^^^^^^^^^^^^^error: aborting due to previous error
For more information about this error, try
rustc --explain E0310. error: Could not compileplayground.
And know I'm out... I read in the book about lifetimes and changed my code a lot with 'a here and 'a in any combination but without luck... I don't want to write down any lifetime definition combination I tried.
I don't understand why I need the lifetime definition. The ownership is moved in any step so for my understanding its clear that Holder struct is the owner for all data. Is it?
How can I correct my code to compile?
Thanks for help.
You almost got it - the issue here is that the type for which BaseStuff is implemented may be a reference (e.g. impl BaseStuff for &SomeType). This means that even though you're passing data by value, the value may be a reference that will be outlived by your Box.
The way to fix this is to add a constraint such that the object has a 'static lifetime, meaning it will either be a value type or a static reference. You can apply this constraint to the trait or the method accepting the trait, depending on your use case.
Applying the constraint to the trait:
trait BaseStuff: 'static {}
Applying the constraint to the method:
impl Holder {
fn register(&mut self, data: impl BaseStuff + 'static) {
self.stuff.push(Box::new(data));
}
}
If you add the 'static constraint to the method, I would recommend also adding it to the Vec to avoid losing type information, like so:
struct Holder {
stuff: Vec<Box<dyn BaseStuff + 'static>>,
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With