2014-02-10 1 views
3

Я пытаюсь реализовать игрушку hashmap в Rust с нуля и получить зацепило при фактической инициализации ведер, которые мне нужны. Я получил эту работу с массивом ведер с другими примитивами, такими как u8 (показано ниже в комментариях ниже).Выделение/копирование вектора строк/векторов в хэш-карте ржавчины?

Я не могу понять, как сказать компилятору выделить меня изменяемый вектор, который содержит другие векторы - в данном случае - ~str. Этот код компилируется, но не работает во время выполнения с ошибкой index out of bounds.

static DEFAULT_NUMBER_OF_BUCKETS: uint = 64; 
static DEFAULT_VALUE_LENGTH: uint = 32; //unused 

struct NaiveHashMap { 
    hashmap_size: uint, //unused. 
    string_capacity: uint, //unused. 
    //contents: ~[ u8 ] 
    contents: ~[ ~str ] 
} 

impl NaiveHashMap { 

    fn new(hash_size: uint, string_size: uint) -> NaiveHashMap { 
     NaiveHashMap { 
      hashmap_size: hash_size, //unused 
      string_capacity: string_size, //unused 
      //contents: ~[ 0, ..DEFAULT_NUMBER_OF_BUCKETS ] 
      contents: std::vec::with_capacity::<~str>(DEFAULT_NUMBER_OF_BUCKETS) 
     } 
    } 

    fn get_hash(&self, key: &str) -> u32 { 
     let hash: u32 = jenkins_hash(key); 
     hash % self.hashmap_size.to_u32().unwrap() 
    } 

    //fn add(&mut self, key: &str, value: u8) { 
    fn add(&mut self, key: &str, value: ~str) { 
     let bucket = self.get_hash(key); 
     self.contents[bucket] = value; 
    } 

    //fn get(self, key: &str) -> u8 { 
    fn get(&self, key: &str) -> ~str { 
     let bucket = self.get_hash(key); 
     self.contents[bucket].clone() 
    } 

} 

Короткого вызов небезопасного выделения from_buf или просто copypasting в станде Hashmap LIB, я не уверен, как поступить.

Я понимаю, что было бы лучше пройти <T> вокруг класса, чтобы он был гибким, но предпочитает сначала вычерчивать этот бит.

EDIT: исправлена ​​ошибка get(), чтобы избежать захвата всей структуры.

ответ

6

Насколько я понимаю, вы хотите построить вектор заданной длины, чтобы написать что-то в него по индексу.

Вы не можете безопасно сделать это в Rust, если не указать значение по умолчанию для каждого элемента вектора. Он работал с ~[u8], потому что вы сделали указать значение по умолчанию (ноль), а потому, что u8 неявно копируемый:

[0, ..DEFAULT_NUMBER_OF_BUCKETS] 

Но что значение по умолчанию можно было бы ожидать ~str? Это указатель, и указатели не могут быть равны нулю в Rust, что было бы самым естественным значением по умолчанию для указателя. Следующим наиболее естественным значением для ~str, я думаю, является ~"", то есть пустая строка. Вы можете использовать его, чтобы создать вектор из N строк:

vec::from_elem(N, ~"") 

Это создаст вектор пустых строк в штучной упаковке. Но это также означает N распределений, а не то, что вы должны делать, не учитывая.

Однако вы не можете сделать это с произвольным типом T, так как обычно произвольный тип T не имеет значения по умолчанию. Кроме того, произвольный T также может быть не Clone, который требуется from_elem(). Но вы можете создать другой тип от T, который имеет значение по умолчанию. Вы можете использовать Option для этого:

contents: ~[Option<T>] 

Чтобы преодолеть не-клонируемости, вы можете использовать from_fn() функцию с крышкой:

contents: vec::from_fn(N, |_| None) 

Кстати, причину он терпит неудачу с «индексом вне границ» ошибка потому что вы используете функцию with_capacity(). Эта функция создает вектор нуля длина, но с указанным вместительностью. Вы можете использовать метод push() для вектора, чтобы добавить элементы к концу, и он не будет перераспределен до тех пор, пока вы не достигнете его емкости, но вы не сможете получить доступ к элементам «снаружи» добавленных.