Hello,
It is related to:
https://doc.rust-lang.org/book/ch17-02-trait-objects.html
And the playground :
It seems only one type is allowed, but the example seems to tell that it compiles without any error!!
Hello,
It is related to:
https://doc.rust-lang.org/book/ch17-02-trait-objects.html
And the playground :
It seems only one type is allowed, but the example seems to tell that it compiles without any error!!
When the Screen
type is not generic — or more precisely, when the field components: Vec<Box<dyn Draw>>
is not generic — the compiler knows what type the Vec
's elements will be, and will automatically figure out that each one in the vec![]
needs to be coerced to Box<dyn Draw>
.
When it is generic, the compiler cannot be certain what you wanted. In fact, your code doesn't mention dyn Draw
anywhere at all, so the compiler is definitely not going to introduce dynamic dispatch without your having asked for it.
A sufficient solution here is to give a type to your variable declaration:
let screen: Screen<Box<dyn Draw>> = Screen {
(In other cases of using vec![]
, you might need to add as Box<dyn Draw>
to one of the vector elements to explicitly coerce it to the wanted type.)
You'll also get an error that Box<dyn Draw>
doesn't implement Draw
, which is true; fix that by adding impl Draw for Box<dyn Draw>
, or even better, a blanket impl for all kinds of Box
es:
impl<T: ?Sized + Draw> Draw for Box<T> {
fn draw(&self) {
(**self).draw()
}
}
Many Rust traits are implemented for Box
es and &
references in this forwarding fashion; it makes them usable in a broader variety of circumstances (not just dyn
) and you should consider adding such impl
s whenever you create a trait.
Yet another option would be to just specify what type of Screen
you want with:
let screen = Screen::<Box<dyn Draw>> {
I wonder which version is considered more ideomatic.
Thank you for your answers.
But I think the doc should explicitly show that the code throws an error.
It's not super clear but the book uses this earlier version of Screen
pub struct Screen {
pub components: Vec<Box<dyn Draw>>,
}
If you define it like this your original code will work too.
Are you saying that when the example is changed from using dyn Draw
to using <T: Draw>
, the doc should say that you can't combine the approaches from the two examples?
It is just showing two different ways of doing things. The two examples are divided by a paragraph starting with "This works differently from defining a struct that uses a generic type parameter with trait bounds...".
Trying to combine the two approaches is something you tried to do, but the doc didn't talk about at all.
No the doc later goes on to use the Screen struct in further examples without showing which definition is used but it's clearly using the first version (the one with dyn Draw
). It could be confusing to some people.
You're right that it could easily be confusing and should be improved.
Looking again, I think there is an error in the paragraph that transitions back from generics to trait objects, just before the next section you referred to:
On the other hand, with the method using trait objects, one
Screen
instance can hold aVec<T>
that contains aBox<Button>
as well as aBox<TextField>
. Let’s look at how this works, and then we’ll talk about the runtime performance implications.
Shouldn't "hold a Vec<T>
" be "hold a Vec<Box<dyn Draw>>
"?
Yeah I saw that too and I think it would already help if this was changed in the way you propose.
It's not technically wrong but it sure is misleading.
Agreed. I'll create a doc issue for it tomorrow.
Noice. Very well written issue.
Sorry for the fight !!