7

&Rc<T>やのようなラッパー型への参照は、 (が a の場合でもaではありません) では&Box<T>不変です。問題の具体例 ( Rust Playground ):T&Rc<T>&Rc<U>TU

use std::rc::Rc;
use std::rc::Weak;

trait MyTrait {}

struct MyStruct {
}

impl MyTrait for MyStruct {}

fn foo(rc_trait: Weak<MyTrait>) {}

fn main() {
    let a = Rc::new(MyStruct {});
    foo(Rc::downgrade(&a));
}

このコードにより、次のエラーが発生します。

<anon>:15:23: 15:25 error: mismatched types:
 expected `&alloc::rc::Rc<MyTrait>`,
    found `&alloc::rc::Rc<MyStruct>`

Box<T>Rust Playground )を使用した同様の例(同様のエラーあり):

trait MyTrait {}

struct MyStruct {
}

impl MyTrait for MyStruct {}

fn foo(rc_trait: &Box<MyTrait>) {}

fn main() {
    let a = Box::new(MyStruct {});
    foo(&a);
}

これらの場合、もちろんa、目的のタイプで注釈を付けることができますが、多くの場合、元のタイプも必要になるため、それは不可能です。それで、私は何をしますか?

4

2 に答える 2

5

ここに表示される内容は、分散やサブタイプとはまったく関係ありません。

まず、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は、特性でありながら、実際に型の位置で使用できますが、特性オブジェクトを形成するためにのみ使用できる特別なサイズのない型を示します。ベア トレイト タイプの値を持つことはできないため、ベア トレイト タイプのサブタイピングとバリアンスについて話すことさえ意味がありません。

特性オブジェクトは&SomeTraitor&mut SomeTraitまたはの形式を取り、変数に渡して格納することができ、特性の実際の実装を抽象化するために必要SmartPointer<SomeTrait>ですただし、&TwhereはのサブタイプでT: SomeTrait はなく&SomeTrait、これらのタイプは分散にまったく関与しません。

特性オブジェクトと通常のポインターは互換性のない内部構造を持っています:&Tは具象型への単なる通常のポインターですがT、は実装する型の元の値へのポインターと、実装のための vtable への 2 番目&SomeTraitのポインターを含むファット ポインターです。前述のタイプの。SomeTraitSomeTrait

&Tas&SomeTraitまたはRc<T>asの受け渡しが機能するという事実はRc<SomeTrait>、Rust が参照とスマート ポインターに対して自動強制を行うためです。これはごく自然なことだと思います。たとえば、あなたの例は、に強制されるタイプの値を返すため、機能します。&SomeTrait&TTRc::downgrade()Rc::downgrade()Weak<MyStruct>Weak<MyTrait>

ただし、if&Box<SomeTrait>からの構築ははるかに複雑です。たとえば、と のメモリ表現が異なるため、コンパイラは新しい一時値を割り当てる必要があります。たとえば、 がある場合、を格納するためにヒープに新しい割り当てを作成する必要があるため、そこから抜け出すのはさらに複雑です。したがって、ネストされた参照とスマート ポインターの自動強制はありません。また、これはサブタイプや分散とはまったく関係ありません。&Box<T>T: SomeTraitBox<T>Box<SomeTrait>Box<Box<T>>Box<Box<SomeTrait>>Box<SomeTrait>

于 2016-06-02T12:52:54.770 に答える
3

この場合、Rc::downgrade実際にはこの特定のケースでの型推論の失敗であり、別の let として実行された場合に機能します。

fn foo(rc_trait: Weak<MyTrait>) {}

fn main() {
    let a = Rc::new(MyStruct {});
    let b = Rc::downgrade(&a);
    foo(b);
}

遊び場

Box<T>ボックスへの参照ではなく、コンテンツへの参照を実際に引数として必要とする可能性が非常に高いためです。その場合、対処する不変性はありません。

fn foo(rc_trait: &MyTrait) {}

fn main() {
    let a = Box::new(MyStruct {});
    foo(a.as_ref());
}

遊び場

同様に、 の場合、Rc<T>を受け取る関数を作成する場合Rc<T>、通常の参照ではなくクローン (つまり、参照カウント参照) が必要になる可能性があります。

fn foo(rc_trait: Rc<MyTrait>) {}

fn main() {
    let a = Rc::new(MyStruct {});
    foo(a.clone());
}

遊び場

于 2016-06-01T23:50:10.493 に答える