🗿

RustのTraitメモ

2023/05/07に公開

個人用メモ。

trait Trait {
    fn m1(&self) -> Self;
    fn m2<T>(&self, value: T);
    fn m3(&self);
}

// fn f(to: &dyn Trait) {} => compile error

上記のm1m2はトレイトオブジェクトでは実行できない。前者は戻り値にトレイトを実装する具体的な型を要求し、後者のジェネリックメソッドはトレイトを実装する具体的な型での呼び出しごとにコンパイル時に静的に解決される。
トレイトオブジェクトは静的な型情報を持っていないのでいずれのメソッドも実行できない。

上記の定義のままではトレイトオブジェクトの作成ができない。上記のケースでは自由関数fの存在のせいでコンパイル自体が通らない。

一方、メソッドm3は実行できるので

trait Trait {
    fn m1(&self) -> Self
    where
        Self: Sized;

    fn m2<T>(&self, value: T)
    where
        Self: Sized;

    fn m3(&self);
}

fn f(to: &dyn Trait) {
    // to.m1(); => compile error
    // to.m2(); => compile error
    to.m3();
}

のように Self: Sized の指定をすることでコンパイル時にサイズを特定できる型にのみ有効なメソッドという指定ができて、トレイトオブジェクトの作成自体は許容されるようになる。

また

trait Trait {
    fn m(&self);
    fn m_take_self(self);
}

fn f(to: Box<dyn Trait>) {
    to.m();
    // to.m_take_self(); => compile error
}

m_take_selfのように所有権をとるメソッドは静的にサイズが判明している事が前提となるためか、ソースにトレイトオブジェクトが出現してもSelf: Sizedの指定は不要だが実行はできない。

これをboxedされたトレイトオブジェクトでも実行可能にする場合は

trait Trait {
    fn m_take_self_for_box(self: Box<Self>);
}

のようにBox<Self>を指定すればよい。Box<Self>以外にRc<Self>, Arc<Self>, Pin<&mut Self>が指定できる模様。

Discussion