ここに表示される内容は、分散やサブタイプとはまったく関係ありません。
まず、Rust でのサブタイプに関する最も有益な読み物は Nomicon のこの章です。Rust では、サブタイプ関係 (つまり、ある型の値を関数または異なる型の変数を期待する変数に渡すことができる場合) が非常に限られていることがわかります。ライフタイムで作業している場合にのみ観察できます。
たとえば、次のコードは、&Box<T>
(co)variant がどの程度正確かを示しています。
fn test<'a>(x: &'a Box<&'a i32>) {}
fn main() {
static X: i32 = 12;
let xr: &'static i32 = &X;
let xb: Box<&'static i32> = Box::new(xr); // <---- start of box lifetime
let xbr: &Box<&'static i32> = &xb;
test(xbr); // Covariance in action: since 'static is longer than or the
// same as any 'a, &Box<&'static i32> can be passed to
// a function which expects &'a Box<&'a i32>
//
// Note that it is important that both "inner" and "outer"
// references in the function signature are defined with
// the same lifetime parameter, and thus in `test(xbr)` call
// 'a gets instantiated with the lifetime associated with
// the scope I've marked with <----, but nevertheless we are
// able to pass &'static i32 as &'a i32 because the
// aforementioned scope is less than 'static, therefore any
// shared reference type with 'static lifetime is a subtype of
// a reference type with the lifetime of that scope
} // <---- end of box lifetime
このプログラムはコンパイルされます。つまり、 と の両方&
がBox
、それぞれの型と有効期間パラメーターに対して共変です。
C++ や Java などのクラス/インターフェースを持つ「従来の」OOP 言語のほとんどとは異なり、Rust では、トレイトはサブタイプ関係を導入しません。といっても、
trait Show {
fn show(&self) -> String;
}
よく似ている
interface Show {
String show();
}
Java のような一部の言語では、セマンティクスがまったく異なります。Rust のベア トレイトは、型として使用される場合、このトレイトを実装する型のスーパータイプになることはありません。
impl Show for i32 { ... }
// the above does not mean that i32 <: Show
Show
は、特性でありながら、実際に型の位置で使用できますが、特性オブジェクトを形成するためにのみ使用できる特別なサイズのない型を示します。ベア トレイト タイプの値を持つことはできないため、ベア トレイト タイプのサブタイピングとバリアンスについて話すことさえ意味がありません。
特性オブジェクトは&SomeTrait
or&mut SomeTrait
またはの形式を取り、変数に渡して格納することができ、特性の実際の実装を抽象化するために必要SmartPointer<SomeTrait>
です。ただし、&T
whereはのサブタイプでT: SomeTrait
はなく&SomeTrait
、これらのタイプは分散にまったく関与しません。
特性オブジェクトと通常のポインターは互換性のない内部構造を持っています:&T
は具象型への単なる通常のポインターですがT
、は実装する型の元の値へのポインターと、実装のための vtable への 2 番目&SomeTrait
のポインターを含むファット ポインターです。前述のタイプの。SomeTrait
SomeTrait
&T
as&SomeTrait
またはRc<T>
asの受け渡しが機能するという事実はRc<SomeTrait>
、Rust が参照とスマート ポインターに対して自動強制を行うためです。これはごく自然なことだと思います。たとえば、あなたの例は、に強制されるタイプの値を返すため、機能します。&SomeTrait
&T
T
Rc::downgrade()
Rc::downgrade()
Weak<MyStruct>
Weak<MyTrait>
ただし、if&Box<SomeTrait>
からの構築ははるかに複雑です。たとえば、と のメモリ表現が異なるため、コンパイラは新しい一時値を割り当てる必要があります。たとえば、 がある場合、を格納するためにヒープに新しい割り当てを作成する必要があるため、そこから抜け出すのはさらに複雑です。したがって、ネストされた参照とスマート ポインターの自動強制はありません。また、これはサブタイプや分散とはまったく関係ありません。&Box<T>
T: SomeTrait
Box<T>
Box<SomeTrait>
Box<Box<T>>
Box<Box<SomeTrait>>
Box<SomeTrait>