web-dev-qa-db-ja.com

トレイトで「Self」を返すのになぜ「Option <Self>」を返すと「Sized」が必要になるのですか?

この特性定義はうまくコンパイルされます:

trait Works {
    fn foo() -> Self;
}

ただし、これによりエラーが発生します。

trait Errors {
    fn foo() -> Option<Self>;
}
error[E0277]: the size for values of type `Self` cannot be known at compilation time
 --> src/lib.rs:6:5
  |
6 |     fn foo() -> Option<Self>;
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `Self`
  = note: to learn more, visit <https://doc.Rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
  = help: consider adding a `where Self: std::marker::Sized` bound
  = note: required by `std::option::Option`

とともに : Sizedスーパートレイトバウンド、動作します。

特性のSelfタイプが自動的にSizedにバインドされないことを知っています。そして私はOption<Self>は、サイズ変更されない限り(スタックを介して)返されません(そのため、Selfのサイズが必要になります)。ただし、戻り値の型としてSelfも同様です。サイズを指定しない限り、スタックに格納することもできません。

最初の特性定義がそのエラーをすでにトリガーしていないのはなぜですか?

この質問 は関連していますが、私が正確に質問に答えることはできません-理解していなければ

13

Optionの問題は氷山の一角に過ぎず、他の人はすでにそのことを少し説明しています。 コメント内の質問 について詳しく説明したいと思います。

SelfSizedではなく、fn foo() -> Selfを実装する方法はありますか?それを行う方法がない場合、SelfバインドなしでSizedを返すことを許可する意味が見当たらないためです。

その方法では、2つの問題があるため、(少なくとも現在のところ)トレイトをトレイトオブジェクトとして利用することは実際には不可能です。

  1. メソッドにはレシーバーがありません

selfパラメータを取らないメソッドは、メソッドテーブルへのポインタを取得する方法がないため、呼び出すことができません。

  1. メソッドは、引数または戻り値の型でSelf型を参照します

これにより、トレイトはオブジェクトセーフではなくなります。つまり、トレイトからトレイトオブジェクトを作成することはできません。

ただし、他の用途には完全に使用できます。

trait Works {
    fn foo() -> Self;
}

#[derive(PartialEq, Debug)]
struct Foo;

impl Works for Foo {
    fn foo() -> Self {
        Foo
    }
}

fn main() {
    assert_eq!(Foo::foo(), Foo);
}
1
ljedrz