Rustトレイトの既定実装と否定実装
概要: SendやSyncなど一部のトレイトで採用されている機能である、既定実装と否定実装の挙動を調べた。
既定実装と否定実装について
既定実装(デフォルト実装, 自動実装, オート実装, default impl, auto impl)と否定実装(negative impl)はRust RFC 0019: Opt-in Builtin Traits (OIBITs)にて、unsafeトレイトとともに規定されている機能である。
OIBITsは、その名前に反して、「言語に組み込みとは限らないマーカートレイトについて、オプトアウト方式での実装を可能にする」という、非常に天邪鬼な機能である。
既定実装と否定実装は組み合わせて使われる。実際の標準ライブラリの例を見るとわかりやすい。
#![feature(optin_builtin_traits)] // unsafeトレイト unsafe trait Send {} // unsafeな既定実装 unsafe impl Send for .. {} // 否定実装 impl<T:?Sized> !Send for Arc<T> {}
標準ライブラリのOIBITs
1.16.0現在、標準ライブラリで既定実装と否定実装が使われているのは以下の4つである。
std::marker::Send
(core::marker::Send
)std::marker::Sync
(core::marker::Sync
)std::panic::UnwindSafe
std::panic::RefUnwindSafe
既定実装
既定実装は以下のような構文を持つ。
unsafe impl Trait for .. {} impl Trait for .. {}
ただし、以下の制約がある。
- この実装は生存期間・型パラメーターをとることができない。
- 実装対象のトレイトは生存期間・型パラメーターをとることができない。
- 既定実装は中身を持つことができない。したがってマーカートレイトに対してしか使うことができない。 (このトレイトがwhere節をもつことは可能である。)
既定実装の適用規則
既定実装をもつトレイトは、トレイト束縛の解決時にその内容が参照される。以下の条件で、既定実装が適用される。 (rustc::traits::select
2104行目)
- 否定実装を含め、他の実装が見つかっていない。 (
rustc::traits::select
1122行目)- where制約を満たしていない実装でも、型のパターンが一致していれば、この時点では「見つかっている」と見なす。
- トレイト自身にwhere制約がある場合、それを満たしている。
- この型を構成する型も、同じトレイトを実装している。ただし、「構成する型」は以下のように定義される。
これらが where
に追加されたのと同様に振る舞うと考えればよい。
既定実装の上書き
上に書いてあるように、既定実装の生成するwhere制約が所望のものではなかったときは、自分で定義した実装で上書きできる。
例えば、
#![feature(optin_builtin_traits)] trait Foo {} impl Foo for .. {} struct B<X>(X);
の場合、 B<X>
には以下の実装が生成されたような扱いになる。
impl<X: Foo> Foo for B<X> {}
例えば、以下のように書くと、 B
に対しては既定実装は適用されなくなり、かわりに明記された実装が使われるようになる。 X
はFooでなくてもよいが、Barである必要があるようになる。
trait Bar {} impl<X: Bar> Foo for B<X> {}
以下の場合、 B<u32>
については上記の実装を適用し、それ以外については B<X>
であっても既定実装を適用することになる。
impl Foo for B<u32> {}
否定実装
否定実装は以下のような構文を持つ。
impl<'a, X> !Trait for Type<'a, X> {}
ただし、以下の制約がある。
- 当たり前だが、inherent implとして (
impl !Type {}
のように) 使うことはできない。 - 実装対象のトレイトは既定実装を持つ必要がある。したがって否定実装は中身を持たないし、マーカートレイトに対してしか使うことができない。 (このトレイトがwhere節をもつことは可能である。)
否定実装は、この実装が採用されたときにエラーとなるという点以外は、普通の実装と同様である。これは既定実装の上書きをするときに、where条件を変えるのではなく実装を丸ごと禁止するのに使う。
まとめ
既定実装と否定実装は Send
/Sync
/UnwindSafe
/RefUnwindSafe
のように、基本的には特定の継承ルールに基づいてマーカートレイトを実装させたいが、特定の型に対しては異なるルールを適用したいときに用いる。