ジェネリックメソッドパラメータを使用してハッシュ可能なトレイトオブジェクト/トレイトオブジェクトを作成するにはどうすればよいですか?

マーク

との両方を実装する構造体がいくつかHashありMyTraitます。私はそれらを&MyTraitトレイトオブジェクトとして使用しています。

今、私&MyTraitも実装したいと思いますHash私はいくつかのことを試しました:

  • 素朴に、trait MyTrait: Hash {}

    the trait `MyTrait` cannot be made into an object
    
  • それから私はこれを試しました:

    impl Hash for MyTrait {
        fn hash<H: Hasher>(&self, hasher: &mut H) {
            // ...
        }
    }
    

    hashでも具体的なタイプの方法に委任する必要があるとself思います。

  • したがって、素朴な次のステップはこれを置くことMyTraitです:

    fn my_hash<H: Hasher>(&self, hasher: &mut H);
    

    これで最初のポイントに戻ります。

  • ジェネリックパラメータの代わりにトレイトオブジェクトを使用することについて何かを読んだので、これを身に付けました MyTrait

    fn my_hash(&self, hasher: &mut H);
    

    次に、これを実際に実装する必要があります。できれば、すべての特性について手作業ではありません。

    impl<T: 'static + Hash> MyTrait for T {
        fn as_any(&self) -> &Any {
            self as &Any
        }
    
        fn my_hash(&self, hasher: &mut Hasher) {
            self.as_any().downcast_ref::<T>().unwrap().hash(hasher)
        }
    }
    

    しかしその後

    the trait bound `std::hash::Hasher: std::marker::Sized` is not satisfied
    `std::hash::Hasher` does not have a constant size known at compile-time
    

    だから私はダウンキャストする必要がありHasherます…

  • ダウンキャストHasherが方法である場合は、にH変換できる汎用パラメーター必要です。Any Hasher試してみましょう。

    trait AnyHasher {
        fn as_any(&self) -> &Any;
    }
    
    impl<H: 'static + Hasher> AnyHasher for H {
        fn as_any(&self) -> &Any {
            self as &Any
        }
    }
    

    そしてダウンキャストする

    impl<T: 'static + Hash, H: 'static + Hasher> MyTrait for T {
        // ...
        fn my_hash(&self, hasher: &mut AnyHasher) {
            let h = hasher.as_any().downcast_ref::<H>().unwrap();
            self.as_any().downcast_ref::<T>().unwrap().hash(h)
        }
    }
    

    しかし悲しいかな

    the type parameter `H` is not constrained by the impl trait, self type, or predicates
    

    私はそれが本当だと思います、しかしそれから私は立ち往生しています。(また、これまでのところ、ちょっとばかげているようです)。

これはできますか?もしそうなら、どのように?

以前PartialEq、トレイトオブジェクトについて質問しましたが、トレイトオブジェクトの具体的なタイプに関する情報が必要なため、大変でした。それはダウンキャストで解決されましたが、私はここでその解決策を適用することができませんでした。

jferard

私はRustの専門家ではありませんが、RustをJavaに変えようとしているようです(気分を害しないでください。私は本当にJavaが好きです)。

ハッシュ可能なトレイトオブジェクトを作成するにはどうすればよいですか?

トレイトオブジェクトのハッシュテーブルを作成するのではなく(簡単です)、トレイトオブジェクトはないトレイトのハッシュテーブルを作成する必要があるため、問題が発生します。

問題

私は要約:あなたは特性を実装するいくつかの様々な構造体を持っているMyTraitHashそしてEq、あなたはのような単一のA hashstableにそれらの混合構造体を入れたいTunedMyTrait形質オブジェクト。これはTunedMyTrait、とのサブトレイトである必要がHashありEqます。しかしMyTrait、特性オブジェクトにTunedMyTraitすることはできますが、できません

理由はご存知だと思いますが、この貴重なリソースを使用して、他の読者にもわかりやすく説明します。(あなたはそれが明確ではないと思われる場合、私は私自身の言葉でそれを置く、それ恥ずかしがりや編集することはありません。)形質オブジェクトは、「オブジェクトの安全性」(参照と呼ばれる何かに依存しているRFC 255)。「オブジェクトの安全性」とは、特性のすべてのメソッドがオブジェクトに対して安全でなければならないことを意味します。

Rustはスタックを集中的に使用するため、可能な限りすべてのサイズを知る必要があります。借用チェッカーの後、それはRustの難しさと美しさの1つです。特性オブジェクトは型指定され、サイズが設定されます。これは、具体的な型に関する情報を含む、ある種の「太い」ポインタです。すべてのメソッド呼び出しは、vtableofメソッドを使用して具象型に委任されます。詳細については説明しませんが、この委任でいくつかの問題が発生する可能性があり、それらの問題を回避するために「安全性チェック」が作成されました。ここに:

  • 実行時にオブジェクトセーフfn eq(&self, other: &Rhs) -> boolRhs = SelfはないメソッドRhsが消去されたため、具体的なタイプとサイズotherが不明です。
  • すべての具象タイプ用に構築されfn hash<H: Hasher>(&self, hasher: &mut H)vtableいるわけではないため、このメソッドはオブジェクトセーフではありませんH

ソリューション

はい。MyTraitは特性オブジェクトですが、そうでTunedMyTraitはありません。ただしTunedMyTrait、ハッシュテーブルの有効なキーはオブジェクトのみである可能性があります。あなたは何ができますか?

あなたがしたように、あなたはオブジェクトの安全メカニズムをハックすることを試みることができます。ハッキングの解決策を見つけPartialEq(キャストトライ、特性オブジェクト間の同等性をテストする方法は?)、@ Boiethiosからの別のハックがあります(これは基本的にハッシュを非汎用関数にします)。最終的に目標を達成した場合、コードの将来の読者を想像することができます。「OMG、この男は何をしようとしているのですか?」または(さらに悪い):「それが何をするのかはわかりませんが、もし...ならもっと速く走ると確信しています。」言語の保護をハッキングしたため、コードによって、解決しようとしている問題よりも悪い問題が発生する可能性があります。これは私にこの種の議論を思い出させます:実行時にジェネリック型のクラスを取得しますその後?このコードで何をしますか?

またはあなたは合理的であることができます。いくつかの可能性があります:実際には同じ具象型のキーを持つハッシュテーブルを使用する、MyTraitオブジェクトをボックス化する、列挙を使用する...他の方法があるかもしれません(言ったように、私はRustの専門家ではありません)。

誤解しないでください。言語をハッキングすることは本当に楽しく、そのメカニズムと制限を深く理解するのに役立ちます(注:その質問をしていなかったら、DSTと特性オブジェクトを詳しく調べていなかったでしょう。ありがとうございます)。しかし、何か深刻なことをするつもりなら、あなたは真剣でなければなりません:RustはJavaではありません...

編集

ランタイム多型のオブジェクトを比較してハッシュしたいと思います。

それは難しいことではありませんが、あなたもそれらをに入れたいと思っていますHashMap、そしてそれは問題です。

私はあなたに別の洞察を与えます。基本的に、ハッシュテーブルはバケットの配列であることを知っています。Rustはオープンアドレス指定を使用してハッシュの衝突(具体的にはRobin Hoodハッシュ)を解決します。つまり、すべてのバケットに0または1のペアが含まれます(key, value)空のバケットにペア(key, value)配置する、の定義に従って、タプル(key, value)がバッファ配列の位置pair_start + index * sizeof::<K, V>()書き込まれます。サイズのペアが必要なことは明らかですoffset

トレイトオブジェクトを使用できる場合は、サイズが変更されたファットポインタがあります。しかし、すでに述べた理由により、それは不可能です。私が提案したすべてのアイデアはこれに焦点を合わせています:サイズ設定されたキーを持っています(値がすでにサイズ設定されていると仮定します)。コンクリートタイプ:明らかにサイズ。ボクシング:ポインタのサイズ。列挙型:最大要素のサイズ+タグのサイズ+パディング。

ボクシングの基本例

警告:インターネットで例を見つけようと懸命に努力しましたが、何も見つかりませんでした。そこで、ボクシングの基本的な例を最初から作成することにしましたが、これが正しい方法かどうかはわかりません。必要に応じてコメントまたは編集してください。

まず、比較可能ハッシュ可能な値で実装MyTraitする具象型のすべてのインスタンスを識別するメソッドをトレイトに追加します。たとえばを返すメソッドを追加しますidi64

trait MyTrait {
    fn id(&self) -> i64; // any comparable and hashable type works instead of i64
}

FooそしてBar、具体的な種類は、(ここで与えられた実装は全く愚かである)、このメソッドを実装します。

struct Foo(u32);

impl MyTrait for Foo {
    fn id(&self) -> i64 {
        -(self.0 as i64)-1 // negative to avoid collisions with Bar
    }
}

struct Bar(String);

impl MyTrait for Bar {
    fn id(&self) -> i64 {
        self.0.len() as i64 // positive to avoid collisions with Foo
    }
}

今、私たちは、実装する必要HashEq置くために、MyTraitHashMapしかし、のためMyTraitそれを行うと、MyTraitサイズが設定されていないため、トレイトオブジェクトにはなり得ないトレイトが得られますBox<Trait>サイズが次のように実装してみましょう

impl Hash for Box<MyTrait> {
    fn hash<H>(&self, state: &mut H) where H: Hasher {
        self.id().hash(state)
    }
}

impl PartialEq for Box<MyTrait> {
    fn eq(&self, other: &Box<MyTrait>) -> bool {
        self.id() == other.id()
    }
}

impl Eq for Box<MyTrait> {}

このidメソッドを使用してとを実装eqしましたhash

さて、考えてみてくださいBox<MyTrait>:1。サイズが決められています。2.実装HashEqます。つまり、HashMap:のキーとして使用できるということです

fn main() {
    let foo = Foo(42);
    let bar = Bar("answer".into());
    let mut my_map = HashMap::<Box<MyTrait>, i32>::new();
    my_map.insert(Box::new(foo), 1);
    my_map.insert(Box::new(bar), 2);

    println!("{:?}", my_map.get(&(Box::new(Foo(42)) as Box<MyTrait>)));
    println!("{:?}", my_map.get(&(Box::new(Foo(41)) as Box<MyTrait>)));
    println!("{:?}", my_map.get(&(Box::new(Bar("answer".into())) as Box<MyTrait>)));
    println!("{:?}", my_map.get(&(Box::new(Bar("question".into())) as Box<MyTrait>)));

}

出力:

    Some(1)
    None
    Some(2)
    None

試してみてくださいhttps//play.integer32.com/?gist = 85edc6a92dd50bfacf2775c24359cd38&version = stable

それがあなたの問題を解決するかどうかはわかりませんが、あなたが何をしようとしているのか本当にわかりません...

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

Related 関連記事

ホットタグ

アーカイブ