2016-04-07 2 views
11

Я использую сложный ключ для HashMap, так что ключ состоит из двух частей, а одна часть - 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()); 
} 

Проблема заключается с окончательным утверждением. Она проходит, но это требует временного распределения кучи, потому что я не могу построить Complex без построения String.

Чтобы устранить временные распределения, подобные этому, Rust предоставляет свойство Borrow, которое использует метод HashMap::get. Я понимаю, как сделать Borrow работы для простых ключей, например, ржавчина стандартной библиотеки PathBuf орудий Borrow<Path> пути использования std::mem::transmute под капотом, но я не могу понять, как заставить его работать на мой 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

Вы смотрели в [Корова] (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

Мне понадобится день или два, чтобы переварить это. Это просто, но это у меня в голове. Моя основная проблема заключается в том, что в моем конкретном случае «Complex» отображается в API моего контейнера, поэтому мне нужно убедиться, что я могу обременять тип с помощью параметра продолжительности жизни, не слишком сильно загрязняя мой интерфейс. Моя первоначальная реакция заключается в том, что функция копирования «Корова» для записи слишком тяжелая, потому что я никогда не делаю никакого копирования, но в некотором смысле я почти прав, потому что иногда я использую экземпляры, а иногда я использую ссылки , После того как я закончу переваривать ваш ответ, я дам вам знать, как это работает. –

+0

Возможно, вам удастся избавиться от параметров времени жизни. См. Мое редактирование. –

+0

Я не могу исключить параметр lifetime, потому что иногда берусь из нестатической 'str'. Тем не менее, мне очень нравится ваша идея, потому что это упростит код в другом месте, потому что новый «Комплекс» охватывает как случаи владения, так и заимствования - нет необходимости в уникальном типе для каждого случая. Одна вещь, которая интригует меня является то, что Руст стандартная библиотека не идти по пути использования 'Cow' в вместо двух типов, чтобы покрыть Обладание и заимствования дела для' Path'/'PathBuf' и др. Идея 'Cow' кажется более обобщенной, потому что она работает и для сложных типов. Это было сделано для эффективности во время выполнения? –

Смежные вопросы