In the following code:
trait Animal {}
struct Dog {}
impl Animal for Dog {}
fn main() {
// first two lines are OK as expected
let _animal1: Box<dyn Animal> = Box::new(Dog {});
let _animal2: Box<dyn Animal> = Box::new(Dog {}) as Box<dyn Animal>;
// next line is a compile error: cast to unsized type: `Dog` as `dyn Animal`
let _animal3: Box<dyn Animal> = Box::new(Dog {} as dyn Animal);
}
Why is it an error to cast Dog{} to dyn Animal?
For this example, compiler correctly infer the type and the cast is redundant. However for the general case, what should be used to provide type information to compiler instead of dyn Animal.
To state the question in a different way: what is the type of object given to Box::new(), if it is not dyn Animal?
As the compiler says, you cannot cast to a dyn type, because it is unsized. That is, a value of type dyn T cannot exist in the stack.
You can only use dyn types behind a pointer-like construct, such as &dyn _ or Box<dyn _>, because the additional level of indirection means that the dyn object itself does not exist, it is only a construction of the type system.
Those handle the inner conversion automatically. As you noticed you don't event need an explicit cast, the coercion is implicit.
These pointer like types do the conversion via the magic CoerceUnsized trait (still unstable).
My understanding is that every reference to an object (e.g. &T, &mut T, Box<T>, Arc<T> etc) can exist without knowing the size of an object (e.g. fulfilling the Sized trait). Which is the case in your first two lines in the example.
However, in the third line, you briefly (before passing it into Box::new()) cast it into dyn Animal, which is not a reference, but instead an object.
Every object that exists in Rust needs to have a known size, because otherwise Rust wouldn't know how much space to allocate for it on the stack. This isn't an issue for references, because references have a constant size independent of the object they are referencing.
Consider this:
let dog = Dog{};
// Works
let _animal : &dyn Animal = &dog as &dyn Animal;
// Fails
let _animal : dyn Animal = dog as dyn Animal;
I think the error message is very explanatory:
error[E0620]: cast to unsized type: `Dog` as `dyn Animal`
--> src/main.rs:13:32
|
13 | let _animal : dyn Animal = dog as dyn Animal;
| ^^^^^^^^^^^^^^^^^
|
help: consider using a box or reference as appropriate
--> src/main.rs:13:32
|
13 | let _animal : dyn Animal = dog as dyn Animal;
| ^^^
For more information about this error, try `rustc --explain E0620`.
The second part of your question is 'what should I use to cast dog into an animal?'.
I'd answer with: It depends on whether or not you want to carry over ownership.
If you do not want to carry over ownership and instead keep the original object:
let dog = Dog{};
let animal: &dyn Animal = &dog;
Else, if you want to move ownership into the animal object:
let dog = Dog{};
let animal: Box<dyn Animal> = Box::new(dog);
Related
I have the following code that tries to take a reference to a trait object from a boxed trait:
trait T {}
struct S {}
impl T for S {}
fn main() {
let struct_box: Box<S> = Box::new(S {});
let struct_ref: &S = &struct_box;
let trait_box: Box<T> = Box::new(S {});
let trait_ref: &T = &trait_box;
}
The compiler returns the following error:
error[E0277]: the trait bound `std::boxed::Box<T>: T` is not satisfied
--> src/main.rs:12:25
|
12 | let trait_ref: &T = &trait_box;
| ^^^^^^^^^^ the trait `T` is not implemented for `std::boxed::Box<T>`
|
= note: required for the cast to the object type `T`
How do I properly borrow &T from Box<T>?
Box<T> implements the AsRef<T> trait, which provides the method as_ref(), so you can turn it into a reference that way:
let trait_ref: &T = trait_box.as_ref();
Normally, deref coercions mean that you don't usually need to write this out explicitly. If you pass a value of type Box<T> to a function that takes &T, the compiler will insert the conversion for you. If you want to call one of the methods on T that takes &self, the compiler will insert the conversion for you. However, deref coercion doesn't apply to a cast to a trait object type (the compiler will choose an unsizing coercion instead, which is what happens in your example).
Borrow the contents of the Box, rather than the Box itself:
let trait_ref: &T = &*trait_box;
The reason the line involving &S works is because the only way for Rust to get from Box<S> to &S is via "deref coercion"; that is, it repeatedly dereferences the value until either the types match, or it can't dereference any further.
Coercing to a trait object, on the other hand, isn't done using dereferencing at all; it involves constructing a new pointer directly from the given one. If it can't do that, it fails.
In Rust, references as well as Box<T>, Rc<T>, and Arc<T> allow the creation of a trait object (e.g., from Box<Type> to Box<dyn Trait>). But is there a way to allow the same conversion with a user-defined generic "smart pointer" type?
For example, MyBox<T> is a thin wrapper around Box<T> but the code below results in a compilation error:
use std::io::Write;
pub fn main() {
let std_box: Box<Vec<u8>> = Box::new(Vec::new());
let std_dyn: Box<dyn Write> = std_box;
// ^ this conversion is allowed.
let my_box: MyBox<Vec<u8>> = MyBox { t: Box::new(Vec::new()) };
let my_dyn: MyBox<dyn Write> = my_box;
// ^ this conversion is not allowed.
}
struct MyBox<T: ?Sized> {
t: Box<T>,
}
error[E0308]: mismatched types
--> traits/src/trait_objs.rs:7:36
|
7 | let my_dyn: MyBox<dyn Write> = my_box;
| ---------------- ^^^^^^ expected trait object `dyn std::io::Write`, found struct `Vec`
| |
| expected due to this
|
= note: expected struct `MyBox<dyn std::io::Write>`
found struct `MyBox<Vec<u8>>`
Unfortunately, it looks like the answer is "you don't" for Rust Stable. Rust is very conservative in which implicit coercions it allows. In particular, from the docs, we see the rule that's kicking in for Box.
Coercion is allowed between the following types:
...
TyCtor(T) to TyCtor(U), where TyCtor(T) is one of
&T
&mut T
*const T
*mut T
Box<T>
and where U can be obtained from T by unsized coercion.
where the relevant unsized coercion rule is
T to dyn U, when T implements U + Sized, and U is object safe.
There's not much room for special casing there, at least not in the current version of Rust.
However, if you're willing to dip into Nightly-only features, then you get the exciting CoerceUnsized trait, whose intended use case is... smart pointers to things which would coerce like a Box would.
#![feature(unsize, coerce_unsized)]
use std::ops::CoerceUnsized;
use std::marker::Unsize;
impl<T, U> CoerceUnsized<MyBox<U>> for MyBox<T> where T: Unsize<U>, U: ?Sized {}
This tells Rust we can coerce MyBox<T> to MyBox<U> if T is "basically the same" as U, for an appropriate definition of "basically the same". No functions are required on the implementation; the trait impl simply needs to exist.
I have the following code that tries to take a reference to a trait object from a boxed trait:
trait T {}
struct S {}
impl T for S {}
fn main() {
let struct_box: Box<S> = Box::new(S {});
let struct_ref: &S = &struct_box;
let trait_box: Box<T> = Box::new(S {});
let trait_ref: &T = &trait_box;
}
The compiler returns the following error:
error[E0277]: the trait bound `std::boxed::Box<T>: T` is not satisfied
--> src/main.rs:12:25
|
12 | let trait_ref: &T = &trait_box;
| ^^^^^^^^^^ the trait `T` is not implemented for `std::boxed::Box<T>`
|
= note: required for the cast to the object type `T`
How do I properly borrow &T from Box<T>?
Box<T> implements the AsRef<T> trait, which provides the method as_ref(), so you can turn it into a reference that way:
let trait_ref: &T = trait_box.as_ref();
Normally, deref coercions mean that you don't usually need to write this out explicitly. If you pass a value of type Box<T> to a function that takes &T, the compiler will insert the conversion for you. If you want to call one of the methods on T that takes &self, the compiler will insert the conversion for you. However, deref coercion doesn't apply to a cast to a trait object type (the compiler will choose an unsizing coercion instead, which is what happens in your example).
Borrow the contents of the Box, rather than the Box itself:
let trait_ref: &T = &*trait_box;
The reason the line involving &S works is because the only way for Rust to get from Box<S> to &S is via "deref coercion"; that is, it repeatedly dereferences the value until either the types match, or it can't dereference any further.
Coercing to a trait object, on the other hand, isn't done using dereferencing at all; it involves constructing a new pointer directly from the given one. If it can't do that, it fails.
I have the following code that tries to take a reference to a trait object from a boxed trait:
trait T {}
struct S {}
impl T for S {}
fn main() {
let struct_box: Box<S> = Box::new(S {});
let struct_ref: &S = &struct_box;
let trait_box: Box<T> = Box::new(S {});
let trait_ref: &T = &trait_box;
}
The compiler returns the following error:
error[E0277]: the trait bound `std::boxed::Box<T>: T` is not satisfied
--> src/main.rs:12:25
|
12 | let trait_ref: &T = &trait_box;
| ^^^^^^^^^^ the trait `T` is not implemented for `std::boxed::Box<T>`
|
= note: required for the cast to the object type `T`
How do I properly borrow &T from Box<T>?
Box<T> implements the AsRef<T> trait, which provides the method as_ref(), so you can turn it into a reference that way:
let trait_ref: &T = trait_box.as_ref();
Normally, deref coercions mean that you don't usually need to write this out explicitly. If you pass a value of type Box<T> to a function that takes &T, the compiler will insert the conversion for you. If you want to call one of the methods on T that takes &self, the compiler will insert the conversion for you. However, deref coercion doesn't apply to a cast to a trait object type (the compiler will choose an unsizing coercion instead, which is what happens in your example).
Borrow the contents of the Box, rather than the Box itself:
let trait_ref: &T = &*trait_box;
The reason the line involving &S works is because the only way for Rust to get from Box<S> to &S is via "deref coercion"; that is, it repeatedly dereferences the value until either the types match, or it can't dereference any further.
Coercing to a trait object, on the other hand, isn't done using dereferencing at all; it involves constructing a new pointer directly from the given one. If it can't do that, it fails.
I tried to create vector of closures:
fn main() {
let mut vec = Vec::new();
vec.push(Box::new(|| 10));
vec.push(Box::new(|| 20));
println!("{}", vec[0]());
println!("{}", vec[1]());
}
That yielded the following error report:
error[E0308]: mismatched types
--> src/main.rs:5:23
|
5 | vec.push(Box::new(|| 20));
| ^^^^^ expected closure, found a different closure
|
= note: expected type `[closure#src/main.rs:4:23: 4:28]`
found type `[closure#src/main.rs:5:23: 5:28]`
= note: no two closures, even if identical, have the same type
= help: consider boxing your closure and/or using it as a trait object
I fixed it by specifying the type explicitly:
let mut vec: Vec<Box<Fn() -> i32>> = Vec::new();
What is the inferred type of vec and why is it that way?
Each closure has an auto-generated, unique, anonymous type. As soon as you add the first closure to the vector, that is the type of all items in the vector. However, when you try to add the second closure, it has a different auto-generated, unique, anonymous type, and so you get the error listed.
Closures are essentially structs that are created by the compiler that implement one of the Fn* traits. The struct contains fields for all the variables captured by the closure, so it by definition needs to be unique, as each closure will capture different numbers and types of variables.
Why can't it just infer Box<Fn() -> i32>?
"can't" is a tough question to answer. It's possible that the compiler could iterate through all the traits of every type that is used to see if some intersection caused the code to compile, but that feels a bit magical to me. You could try opening a feature request or discussing it on one of the forums to see if there is general acceptance of such an idea.
However, Rust does try to make things explicit, especially things that might involve performance. When you go from a concrete struct to a trait object, you are introducing indirection, which has the possibility of being slower.
Right now, the Fn* traits work the same as a user-constructed trait:
trait MyTrait {
fn hello(&self) {}
}
struct MyStruct1;
impl MyTrait for MyStruct1 {}
struct MyStruct2;
impl MyTrait for MyStruct2 {}
fn main() {
let mut things = vec![];
things.push(MyStruct1);
things.push(MyStruct2);
}
error[E0308]: mismatched types
--> src/main.rs:14:17
|
14 | things.push(MyStruct2);
| ^^^^^^^^^ expected struct `MyStruct1`, found struct `MyStruct2`
|
= note: expected type `MyStruct1`
found type `MyStruct2`