Rustでジェネリック関連型/関連型コンストラクターをシミュレートする方法はありますか?

LVB

Rustでグラフ処理モジュールを作っています。モジュールのコアは、グラフ内のデータを保持する複数のコンテナーを持つという考えをモデル化しています。たとえば、内部構造がHashMapまたはAdjacencyMatrixなどであるグラフがある場合があります

これらのコンテナは、特性を実装する必要があります。

trait GraphData<V> {
    fn has_edge(&self, v: &V, u: &V) -> bool;
    fn nodes(&self) -> Iterator<V>; // Here's the problem...
}

トレイト定義でトレイトを返すことはできません。トレイトオブジェクトを使用する必要があることはわかっていますが、使用したくありませんBoxコンテナに独自のNodeIter構造体を提供させたいのですが。ただし、関連する型コンストラクター、パート1:基本的な概念と概要で説明されているのと同じ問題に悩まさます。投稿では、現在Rustに存在しない関連する型コンストラクター(ATC)について説明しています。GraphDataCollection説明されているジェネリックに似ています。

ATCを「シミュレート」するために使用できる回避策、またはこの状況で使用できるRustに固有のパターンはありますか?

動的ディスパッチに依存せずBox、またはdynキーワードを使用することに頼りたくありません

NodeIterモジュールで作成したグラフコンテナのタイプごとに構造体を定義し、コンテナ自体の実装内に「ノード」を追加することを考えましたただし、これはコードの再利用が不十分であることがわかりました。

Lukas Kalbertodt

以下のようアンダースKaseorgによって答えはすでに説明:あなたはあなたのクローニングと一緒に暮らすことができる場合は、ここではGATSを必要としないかもしれないVec頂点を含みます。しかし、それはおそらくあなたが望むものではありません。代わりに、通常、元のデータを参照するイテレータが必要です。

これを実現するには、実際にはGATを使用するのが理想的です。しかし、それらはまだ言語の一部ではないので、あなたの主な質問に取り組みましょう:ジェネリック関連型をシミュレートする方法はありますか?私は実際にこのトピックについて非常に広範なブログ投稿を書きました:「GATなしで一般化されたストリーミングイテレータ問題を解決する」

記事の要約:

  • あなたにとって最も簡単な方法は、イテレータをボックス化し、それをトレイトオブジェクトとして返すことです。

    fn nodes(&self) -> Box<dyn Iterator<&'_ V> + '_>
    

    あなたが言ったように、あなたはそれを望まないので、それは終わりです。

  • トレイトにライフタイムパラメーターを追加し、そのライフタイムを関連するタイプと&selfレシーバーで使用できます

    trait GraphData<'s, V: 's> {
        type NodesIter: Iterator<Item = &'s V>;
        fn nodes(&'s self) -> Self::NodesIter;
    }
    
    struct MyGraph<V> {
        nodes: Vec<V>,
    }
    
    impl<'s, V: 's> GraphData<'s, V> for MyGraph<V> {
        type NodesIter = std::slice::Iter<'s, V>;
        fn nodes(&'s self) -> Self::NodesIter {
            self.nodes.iter()
        }
    }
    

    これはうまくいきます!しかし、今あなたはあなたの特性に迷惑な生涯パラメータを持っています。あなたの場合、それは(煩わしさは別として)問題ないかもしれませんが、状況によっては実際には重大な問題になる可能性があるため、これがうまくいく場合とうまくいかない場合があります。

  • ライフタイムからタイプへのタイプレベル関数として機能するヘルパートレイトを使用することで、ライフタイムパラメーターをより深いレベルにプッシュできます。これにより、ライフタイムパラメータがメインの特性に含まれなくなったため、状況が少し煩わしくなくなりますが、以前の回避策と同じ制限があります。

  • まったく別のパスに移動して、グラフへの参照を含むイテレーターラッパーを作成することもできます。

    これは大まかなスケッチですが、基本的な考え方は機能します。実際の内部イテレータにはグラフへの参照が含まれていません(したがって、そのタイプにはself有効期間は必要ありません)。代わりに、グラフ参照は特定のタイプに格納され、呼び出しWrapごとに内部イテレータに渡されnextます。

    このような:

    trait InnerNodesIter { /* ... */ }
    
    struct Wrap<'graph, G: GraphData, I: InnerNodesIter> {
        graph: &'graph G,
        iter: I,
    }
    
    type NodesIterInner: InnerNodesIter;
    fn nodes(&self) -> Wrap<'_, Self, Self::NodesIterInner>;
    

    次に、あなたが実装することができますIteratorのためにWrapグラフへの参照を渡すことができる内部イテレータへのインターフェイスが必要です。のようなものfn next(&mut self, graph: &Graph) -> Option<...>でインターフェースを定義する必要がありますInnerNodesIter

    もちろん、これは非常に冗長であることに苦しんでいます。また、イテレータの動作によっては、少し遅くなる場合もあります。

短くて悲しい要約は次のとおりです。すべての状況で機能する満足のいく回避策はありません。


この場合の私の意見:私はこの正確な状況が複数回発生したプロジェクトに取り組んでいます。私の場合、Box非常に簡単で問題なく機能するソリューションを使用しました唯一の欠点は速度(割り当てと動的ディスパッチ)ですが、割り当てはタイトなループでは発生せず(グラフが多数あり、それぞれのノードが非常に少ない場合を除く)、オプティマイザーはおそらく機能しますほとんどの場合、動的呼び出しを非仮想化します(結局のところ、実際の型情報は1つの関数境界だけ離れています)。

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

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

編集
0

コメントを追加

0

関連記事

プロトコルを修正するにはどうすればよいですか?プロトコルには自己または関連する型要件エラーがあるため、ジェネリック制約としてのみ使用できます

関連付けられた値を持つプロトコルに準拠するジェネリック型のインスタンスまたは戻り型をどのように持つことができますか

Rustの関数のジェネリック型としてジェネリック型エイリアスを使用する方法はありますか

関数型プログラミング言語には、オブジェクトの一部を変更するためのシンタックスシュガーがありますか?

Rスクリプトで乱数ジェネレーターを使用する関数をチェックする包括的な方法はありますか?

コンストラクターで繰り返しジェネリック型を指定しますか?

resultMapは常に、「コンストラクター、ID、結果、関連付け、コレクション、ディスクリミネーターと一致する必要があります」と報告します。

ジェネリック型でコンストラクターインジェクションを使用することは可能ですか?

タッチスクリーン以外のユーザーのジェスチャーをシミュレートする方法はありますか?

タッチスクリーン以外のユーザーのジェスチャーをシミュレートする方法はありますか?

Rustのジェネリック型パラメーターに依存する構造体に関連付けられた関数のさまざまな実装を定義するにはどうすればよいですか?

「プロトコル…には自己または関連する型の要件があるため、ジェネリック制約としてのみ使用できます」というエラーが表示されるのはなぜですか?

ジェネリック型を一連の型のメンバーになるように制約する方法はありますか?

ジェネリック型にコンストラクターがあることをTypeScriptに伝えることはできますか?

Rustで文字列リテラル型をエミュレートする方法はありますか?

Javaでゲームをプログラミングする。関連するディレクトリからクラスファイルを読み取り、それらから作成されたオブジェクトを返す必要があります

コンストラクターがデリゲート関数パラメーターを必要とするジェネリック型のインスタンスを作成するにはどうすればよいですか?

Javaのコンストラクターでジェネリック列挙型を割り当てる方法は?

ジェネリック型でコンストラクター/キャストを使用する方法

リフレクション以外のジェネリック型のインスタンスをScalaで作成する方法はありますか?

データをc ++例外クラスにストリーミングすることに関連する危険はありますか?

プロトコルおよび関連する型でジェネリック型を使用していますか?

関数型プログラミングのディープスタックは、JVMでのガベージコレクションを防ぎますか?

C ++の親オブジェクトは子オブジェクトの内部データを失います。コピーコンストラクターまたは参照による受け渡しに関連して何か問題がありますか?

リストは生の型です。ジェネリック型リスト <E> への参照はパラメータ化する必要があります

クラスが互いに関連していないが、ソートが必要な共通のプロパティがある場合は、ジェネリックコンパレータを作成します

ジェネリックス-関数の戻り型はパラメータ型と同じですか?

関数型プログラミングに関するいくつかの本では、インスタンスメソッドはコンパニオンオブジェクトで定義されたバイナリ関数に委任します。その背後にある実用的な理由はありますか?

関連する型のジェネリック境界を持つ関数アイテムのコレクションを反復処理します

TOP 一覧

  1. 1

    モーダルダイアログを自動的に閉じる-サーバーコードが完了したら、Googleスプレッドシートのダイアログを閉じます

  2. 2

    セレンのモデルダイアログからテキストを抽出するにはどうすればよいですか?

  3. 3

    CSSのみを使用して三角形のアニメーションを作成する方法

  4. 4

    ドロップダウンリストで選択したアイテムのQComboBoxスタイル

  5. 5

    ZScalerと証明書の問題により、Dockerを使用できません

  6. 6

    PyCharmリモートインタープリターはプロジェクトタブにサイトパッケージのコンテンツを表示しません

  7. 7

    Windows 10でのUSB入力デバイスの挿入/取り外しの検出

  8. 8

    Excel - count multiple words per cell in a range of cells

  9. 9

    PictureBoxで画像のブレンドを無効にする

  10. 10

    Windows 10 Pro 1709を1803、1809、または1903に更新しますか?

  11. 11

    スタート画面にシャットダウンタイルを追加するにはどうすればよいですか?

  12. 12

    Python / SciPyのピーク検出アルゴリズム

  13. 13

    Luaの文字列から特定の特殊文字を削除するにはどうすればよいですか?

  14. 14

    Pythonを使用して、リストからデータを読み取り、特定の値をElasticsearchにインデックス付けするにはどうすればよいですか?

  15. 15

    LinuxでPySide2(Qt for Python)をインストールするQt Designerはどこにありますか?

  16. 16

    goormIDEは、ターミナルがロードするデフォルトプロジェクトを変更します

  17. 17

    QGISとPostGIS(マップポイント(米国の地図上にraduisを使用した緯度と経度)

  18. 18

    MLでのデータ前処理の背後にある直感

  19. 19

    ターミナルから「入力ソースの変更」ショートカットを設定する

  20. 20

    パンダは異なる名前の列に追加します

  21. 21

    同じクラスの異なるバージョンを使用したクラスローディング:java.lang.LinkageError:名前の重複クラス定義を試行しました

ホットタグ

アーカイブ