Rust で特性を「サブクラス化」する 質問する

Rust で特性を「サブクラス化」する 質問する

いくつかの構造体が複数の特性を実装する必要がある状況がありますが、それらはすべて少なくとも 1 つの共通の特性を実装しています。これらの構造体の混合バッグを入手した場合、それらをすべて共通の特性として扱います。つまり、その特性に型付けされたメソッド パラメーターとして渡し、その特性に型付けされたコレクションに格納するなどします。

どうすればいいのかわかりません。ここで提案されている方法を試してみたのですが、コンパイルに失敗したコードがいくつかあります。

trait ThingWithKeys {
    fn use_keys (&self) -> String;
}

//////

trait CorrectionsOfficer {
    fn hitch_up_pants (&self) -> String;
}

trait CorrectionsOfficerWithKeys: ThingWithKeys + CorrectionsOfficer {}

struct CorrectionsOfficerReal {}

impl ThingWithKeys for CorrectionsOfficerReal {
    fn use_keys (&self) -> String {
        String::from ("Clank, clank")
    }
}

impl CorrectionsOfficer for CorrectionsOfficerReal {
    fn hitch_up_pants (&self) -> String {
        String::from ("Grunt")
    }
}

impl <T: ThingWithKeys + CorrectionsOfficer> CorrectionsOfficerWithKeys for T {}

//////

trait Piano {
    fn close_lid (&self) -> String;
}

trait PianoWithKeys: Piano + ThingWithKeys {}

struct PianoReal {}

impl ThingWithKeys for PianoReal {
    fn use_keys (&self) -> String {
        String::from ("Tinkle, tinkle")
    }
}

impl Piano for PianoReal {
    fn close_lid (&self) -> String {
        String::from ("Bang!")
    }
}

impl <T: ThingWithKeys + Piano> PianoWithKeys for T {}

//////

trait Florida {
    fn hurricane (&self) -> String;
}

trait FloridaWithKeys: ThingWithKeys + Florida {}

struct FloridaReal {}

impl ThingWithKeys for FloridaReal {
    fn use_keys (&self) -> String {
        String::from ("Another margarita, please")
    }
}

impl Florida for FloridaReal {
    fn hurricane (&self) -> String {
        String::from ("Ho-hum...")
    }
}

impl <T: ThingWithKeys + Florida> FloridaWithKeys for T {}

//////

fn main() {
    let corrections_officer_ref: &CorrectionsOfficerWithKeys = &CorrectionsOfficerReal {};
    let piano_ref: &PianoWithKeys = &PianoReal {};
    let florida_ref: &FloridaWithKeys = &FloridaReal {};

    use_keys (corrections_officer_ref);
    use_keys (piano_ref);
    use_keys (florida_ref);
}

fn use_keys (thing_with_keys: &ThingWithKeys) {
    println! ("{}", thing_with_keys.use_keys ());
}

コンパイル エラーは次のとおりです。

Compiling playground v0.0.1 (file:///playground)
error[E0308]: mismatched types
  --> src/main.rs:80:19
   |
80 |         use_keys (corrections_officer_ref);
   |                   ^^^^^^^^^^^^^^^^^^^^^^^ expected trait `ThingWithKeys`, found trait `CorrectionsOfficerWithKeys`
   |
   = note: expected type `&ThingWithKeys`
              found type `&CorrectionsOfficerWithKeys`

error[E0308]: mismatched types
  --> src/main.rs:81:19
   |
81 |         use_keys (piano_ref);
   |                   ^^^^^^^^^ expected trait `ThingWithKeys`, found trait `PianoWithKeys`
   |
   = note: expected type `&ThingWithKeys`
              found type `&PianoWithKeys`

error[E0308]: mismatched types
  --> src/main.rs:82:19
   |
82 |         use_keys (florida_ref);
   |                   ^^^^^^^^^^^ expected trait `ThingWithKeys`, found trait `FloridaWithKeys`
   |
   = note: expected type `&ThingWithKeys`
              found type `&FloridaWithKeys`

error: aborting due to 3 previous errors

本質的には、XxxWithKeys 実装内で ThingWithKeys 実装を見つけることができません。

ベストアンサー1

Rust のトレイト継承は OOP 継承とは異なります。トレイト継承は要件を指定するための方法にすぎません。trait B: A 意味しない型が実装すればB自動的に実装されるA。つまり、型が実装すれB実装する必要がある Aこれはまた、実装する必要があることを意味しますA 別々に実装されている場合B

例えば、

trait A {}
trait B: A {}

struct S;

impl B for S {}

// Commenting this line will result in a "trait bound unsatisfied" error
impl A for S {}

fn main() {
    let _x: &B = &S;
}

ただし、型が および を実装している場合に自動的に を実装したい場合(Cそれによってその型を手動で実装する必要がなくなる場合)、ジェネリック を使用できます。ABCimpl

impl<T: A + B> C for T {}

あなたの例では、これは次のように翻訳されます

impl<T: Florida + ThingWithKeys> FloridaWithKeys for T {}

を見てみましょうこのフォーラムスレッド詳細については。

余談ですが、ではすでに が必要であるため、ThingWithKeysの境界は必要ありません。PianoWithKeysPianoThingWithKeys

編集(あなたのコメントと質問の編集に従って):

前述の通り、Rust の特性継承は OOP 継承とは異なります。の場合でも、 の特性オブジェクトをの特性オブジェクトにtrait B: A強制変換することはできません。特性オブジェクトをそのまま メソッドに渡す以外に選択肢がない場合は、ジェネリックを使用するとうまくいきます。BA

fn use_keys<T: ThingWithKeys + ?Sized>(thing_with_keys: &T) {
    println! ("{}", thing_with_keys.use_keys ());
}

ジェネリックメソッドは、型参照 (非特性オブジェクト) でも機能します。

以下も確認してください:Rust はなぜ特性オブジェクトのアップキャストをサポートしないのですか?

おすすめ記事