Trait implementing Sized

2020-08-11 04:17发布

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.

标签: rust
2条回答
【Aperson】
2楼-- · 2020-08-11 04:42

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.

查看更多
仙女界的扛把子
3楼-- · 2020-08-11 04:48

The Foo trait requires implementors to be Sized. It doesn't mean that Foo itself will have a size. You're misunderstanding the syntax the second code example and I'm therefore not sure what you're actually trying to do. Are you looking for this?

impl From<i64> for Bar {
    fn from(val: i64) -> Bar {
        Bar(val)
    }
}

Or do you want a way to construct a Bar from any signed integer?

We've got a XY problem here.

查看更多
登录 后发表回答