2016-04-07 4 views
11

Ich verwende einen komplexen Schlüssel für HashMap derart, dass der Schlüssel aus zwei Teilen bestehen und ein Teil ist ein String, und ich kann nicht herausfinden, wie Lookups über die HashMap::get Methode zu tun, ohne eine neues String für jeden lookup zuzuordnet.Wie vermeidet man temporäre Zuordnungen bei der Verwendung eines komplexen Schlüssels für HashMap?

Hier einige Code:

#[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()); 
} 

Das Problem mit der letzten Behauptung ist. Es besteht, aber es erfordert eine temporäre Heap-Zuordnung, da ich kein Complex konstruieren kann, ohne ein String zu erstellen.

Um vorübergehende Zuweisungen wie diese zu beseitigen, bietet Rust die Borrow Eigenschaft, die die HashMap::get Methode verwendet. Ich verstehe, wie Borrow Arbeit für einfache Tasten, zum Beispiel zu machen, der Rust Standard Library des PathBuf implementiert Borrow<Path> durch die Verwendung von std::mem::transmute unter der Herstellung hauben aber ich kann nicht herausfinden, wie man es für meine Complex Art funktioniert:

#[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!(); 
    } 
} 

Dies scheint wie ein häufiger Anwendungsfall, und ich vermute, ich vermisse etwas Wichtiges über Borrow, aber ich bin bei einem Totalverlust.

+0

Haben Sie sich [Kuh] (https://doc.rust-lang.org/std/borrow/enum.Cow.html) angesehen? – Aaronepower

Antwort

5

Es klingt wie du willst.

Cow akzeptiert eine &str oder 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()); 
} 

Ein Kommentar über Lebensdauer Parameter:

Wenn Sie die Lebensdauer Parameter nicht und Sie müssen nur mit &'static str oder String arbeiten, dann können Sie Cow<'static, str> und entfernen Sie die anderen Lebensdauer Parameter aus der impl verwenden Block- und Strukturdefinition.

+0

Ich brauche einen oder zwei Tage um das zu verdauen. Es ist einfach, aber es brennt mir durch den Kopf. Mein Hauptanliegen ist, dass "Complex" in meinem speziellen Fall in der API meiner Kiste verfügbar ist, also muss ich sicherstellen, dass ich den Typ mit einem lebenslangen Parameter belasten kann, ohne meine Schnittstelle zu sehr zu beschmutzen. Meine erste Reaktion ist, dass Cows Copy-on-Write-Funktionalität zu schwer ist, weil ich nie kopiere, aber in gewisser Weise bin ich fast, weil ich manchmal Instanzen verwende und manchmal Referenzen verwende . Nachdem ich deine Antwort verdaut habe, werde ich dich wissen lassen, wie es funktioniert. –

+0

Sie könnten die Lebensdauerparameter loswerden. Siehe meine Bearbeitung. –

+0

Ich kann den Lebensdauer-Parameter nicht eliminieren, weil ich manchmal von einem nicht statischen 'str' borgen kann. Nichtsdestotrotz mag ich Ihre Idee sehr, weil sie den Code anderswo vereinfachen würde, weil der neue Komplex sowohl die Besitz- als auch die Ausleihfälle abdeckt - es ist nicht notwendig, für jeden Fall einen eindeutigen Typ zu verwenden. Eine Sache, die mich fasziniert, ist, dass die Rust Standard Library nicht den Weg der Verwendung von "Cow" anstelle von zwei Typen gegangen ist, um die Besitz- und Ausleihfälle für 'Path' /' PathBuf' et al. Die Kuh-Idee scheint generalisierter, weil sie auch für komplexe Typen funktioniert. Wurde dies für die Laufzeit-Effizienz getan? –

Verwandte Themen