Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trait implementing Sized

Tags:

rust

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.

like image 337
jojo Avatar asked Jul 20 '15 11:07

jojo


People also ask

What is a trait object?

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.

How do you implement a 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.

What does sized mean in Rust?

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.


1 Answers

Why doesn't it work?

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.

How to solve it?

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.

like image 194
mdup Avatar answered Oct 16 '22 07:10

mdup