I know that traits and slices are unsized, i.e. it's not possible to know their size at compile time, e.g. any type may implement a trait, but that type may not be sized.
Nevertheless, doesn't this example code mean that every type which implements trait Foo
needs to implement Sized
too?
trait Foo: Sized {}
struct Bar(i64);
impl Foo for Bar {}
If that's so, why doesn't this work?
impl From<Foo> for Bar {
fn from(foo: Foo) -> Bar {
Bar(64)
}
}
error[E0277]: the trait bound `Foo + 'static: std::marker::Sized` is not satisfied
--> src/main.rs:7:6
|
7 | impl From<Foo> for Bar {
| ^^^^^^^^^ `Foo + 'static` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `Foo + 'static`
I want to provide to the consumer of the library a type (let's name it Bar
) and make it possible to convert to Bar
from any other type which implements a particular trait (let's name it Foo
).
I solved it by passing Foo
by the reference instead of the value, but I'm not sure why the compiler complains if it's required for implementors to be Sized
.
A trait object is an opaque value of another type that implements a set of traits. The set of traits is made up of an object safe base trait plus any number of auto traits. Trait objects implement the base trait, its auto traits, and any supertraits of the base trait.
To implement a trait, declare an impl block for the type you want to implement the trait for. The syntax is impl <trait> for <type> . You'll need to implement all the methods that don't have default implementations.
The Sized trait in Rust is an auto trait and a marker trait. Auto traits are traits that get automatically implemented for a type if it passes certain conditions. Marker traits are traits that mark a type as having a certain property.
When you say that every Foo
is Sized
, you're kind of hiding the truth to yourself. Yes, every Foo
is Sized
but actually every type has a given size at some point. The real important information is that you're not saying how much this size is. Imagine if Bar(i64)
is Foo
, but Baz(i8)
is Foo
as well (they're both Sized
, right?) which size do you determine Foo
to be? Is it 8- or 1-byte long? This question is asked by the compiler when it tries to generate the code for your function from(foo: Foo)
. Usually, Sized
is rather used in a "maybe"-style with the syntax ?Sized
, indicating that the type size might be unknown at compile time.
Typically you ditch the : Sized
part, and use the following syntax, which is actually kind of a C++ template; it gives the compiler a sketch to write the actual code when given a concrete type with a given size.
trait Foo {}
struct Bar(i64);
impl Foo for Bar {}
impl<F: Foo> From<F> for Bar {
fn from(foo: F) -> Bar {
Bar(64)
}
}
(This will still error based on the fact that you cannot reimplement From
because of the std
crate, but it's not related to your original question.)
You could also use the reference trait object &Foo
syntax in the argument to your function. This would transform your call from static dispatch to dynamic dispatch (read more here) but you can't do this here because the signature is imposed by the trait.
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