🗿
RustのTraitメモ
個人用メモ。
trait Trait {
fn m1(&self) -> Self;
fn m2<T>(&self, value: T);
fn m3(&self);
}
// fn f(to: &dyn Trait) {} => compile error
上記のm1
とm2
はトレイトオブジェクトでは実行できない。前者は戻り値にトレイトを実装する具体的な型を要求し、後者のジェネリックメソッドはトレイトを実装する具体的な型での呼び出しごとにコンパイル時に静的に解決される。
トレイトオブジェクトは静的な型情報を持っていないのでいずれのメソッドも実行できない。
上記の定義のままではトレイトオブジェクトの作成ができない。上記のケースでは自由関数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