2016-04-07 13 views
11

私はの複雑なキーを使用しています。そのキーは2つの部分で構成され、1つの部分はStringです。HashMap::getメソッドを使用してルックアップを行う方法を見つけることができません。新しいルックアップごとにStringを割り当てません。HashMapに複雑なキーを使用するときに一時的な割り当てを避けるには?

#[derive(Debug, Eq, Hash, PartialEq)] 
struct Complex { 
    n: i32, 
    s: String, 
} 

impl Complex { 
    fn new<S: Into<String>>(n: i32, s: S) -> Self { 
     Complex { 
      n: n, 
      s: s.into(), 
     } 
    } 
} 

fn main() { 
    let mut m = std::collections::HashMap::<Complex, i32>::new(); 
    m.insert(Complex::new(42, "foo"), 123); 

    // OK, but allocates temporary String 
    assert_eq!(123, *m.get(&Complex::new(42, "foo")).unwrap()); 
} 

問題は、最終的な主張である:

はここにいくつかのコードです。それは成功しますが、Stringを構築せずにComplexを構築することができないため、一時的なヒープ割り当てが必要です。

このような一時的な割り当てをなくすために、Rustは特性を提供します。これはHashMap::getメソッドが使用します。

:私は下 std::mem::transmuteを利用して、簡単なキー。たとえば、錆標準ライブラリの PathBuf実装 Borrow<Path>ため Borrow仕事を作る方法を理解フード-が、私はそれが私の Complexタイプのために働くようにする方法を見つけ出すことはできません
#[derive(Debug)] 
struct Borrowable { 
    // ??? -- What goes here? Perhaps something like: 
    n: i32, 
    s1: &str, // ??? -- But what would the lifetime be? Or maybe: 
    s2: str, // ??? -- But how would I extend this to a complex type 
       //  containing two or more strings? 
} 

impl Borrowable { 
    fn new(n: i32, s: &str) -> &Self { 
     // ??? -- What goes here? It must not allocate. 
     unimplemented!(); 
    } 
} 

impl std::borrow::Borrow<Borrowable> for Complex { 
    fn borrow(&self) -> &Borrowable { 
     // ??? -- What goes here? How can I transmute a Complex into a 
     //  &Borrowable? 
     unimplemented!(); 
    } 
} 

これは一般的な使用例のようですが、私はBorrowについて重要な何かを見逃していると思われますが、私は完全に迷っています。

+0

[Cow](https://doc.rust-lang.org/std/borrow/enum.Cow.html)を調べましたか? – Aaronepower

答えて

5

あなたがこれを望むように聞こえます。

Cowは、&strまたはStringを受け入れます。

use std::borrow::Cow; 

#[derive(Debug, Eq, Hash, PartialEq)] 
struct Complex<'a> { 
    n: i32, 
    s: Cow<'a, str>, 
} 

impl<'a> Complex<'a> { 
    fn new<S: Into<Cow<'a, str>>>(n: i32, s: S) -> Self { 
     Complex { 
      n: n, 
      s: s.into(), 
     } 
    } 
} 

fn main() { 
    let mut m = std::collections::HashMap::<Complex, i32>::new(); 
    m.insert(Complex::new(42, "foo"), 123); 

    assert_eq!(123, *m.get(&Complex::new(42, "foo")).unwrap()); 
} 

寿命パラメータについてのコメント:

あなたが寿命パラメータを好きではないし、あなただけ&'static strまたはStringで作業する必要がある場合、あなたはCow<'static, str>を使用し、独自の実装から他の寿命パラメータを削除することができますブロックと構造体の定義。

+0

これを消化するには、1日か2日が必要です。それは簡単ですが、それは私の心を吹き飛ばします。私の主な関心事は、私の特定のケースでは 'Complex'が私のクレートのAPIに公開されているので、私はインターフェイスをあまりにも泥沼させることなく、生涯パラメータで型に負担をかけられるようにする必要があるということです。私の最初の反応は、「Cow」のコピーオンライト機能が重すぎるということです。なぜなら、私は決してコピーをしていないからです。しかし、ある意味では、時々私はインスタンスを使用しています。 。あなたの答えを消化したら、それがどのように機能するかを教えてあげます。 –

+0

寿命のパラメータを取り除くことができます。私の編集を参照してください。 –

+0

私は静的でない 'str'から時々借りるので、私は寿命パラメータを取り除くことができません。それにもかかわらず、私は本当にあなたのアイデアが好きです。なぜなら新しい「Complex」は所有ケースと借用ケースの両方をカバーするためです。各ケースごとに固有のタイプを必要としないためです。私に興味をそそられているのは、錆スタンダード図書館は、 'Path' /' PathBuf'外人の所有と借り入れの2つのタイプをカバーする代わりに 'Cow'を使用する道を進んでいないということです。 'Cow'の考え方は複雑な型でも機能するので、より一般的に思えます。これは実行時の効率化のために行われましたか? –

関連する問題