Rust на примерах — страница 55 из 65

$ rustc panic.rs && valgrind ./panic

==4401== Memcheck, a memory error detector

==4401== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.

==4401== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for copyright info

==4401== Command: ./panic

==4401==

thread '
' panicked at 'division by zero', panic.rs:5

==4401==

==4401== HEAP SUMMARY:

==4401==     in use at exit: 0 bytes in 0 blocks

==4401==   total heap usage: 18 allocs, 18 frees, 1,648 bytes allocated

==4401==

==4401== All heap blocks were freed -- no leaks are possible

==4401==

==4401== For counts of detected and suppressed errors, rerun with: -v

==4401== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

HashMap

В то время как вектора сохраняют значения с числовыми индексами, HashMap сохраняют значения по ключу. Ключи HashMap могут иметь логический, числовой, строковый или любой другой тип данных, который реализует типажи Eq и Hash. Подробнее об этом в следующей главе.

Как и вектора, HashMap расширяемые, но они также могут и сжать себя, когда у них появляется избыточное пространство. Вы можете создать хэш-карту с определённой размерностью при помощи HashMap::with_capacity(uint) или использовать HashMap::new() для получения хэш-карты с размерностью по умолчанию (рекомендуется).

use std::collections::HashMap;

fn call(number: &str) ->&str {

match number {

"798-1364" => "Абонент выключен или находится вне зоны действия сети.

Пожалуйста, позвоните позднее.",

"645-7689" => "Здравствуйте, это Mr. Awesome's Pizza. Меня зовут Фред.

Что я могу сделать для вас?",

_ => "Привет! Кто это опять?"

}

}

fn main() {

let mut contacts = HashMap::new();

contacts.insert("Даниель", "798-1364");

contacts.insert("Эшли", "645-7689");

contacts.insert("Кейти", "435-8291");

contacts.insert("Роберт", "956-1745");

// Возьмём ссылку и вернём `Option<&V>`

match contacts.get(&"Даниель") {

Some(&number) => println!("Звоним Даниелю: {}", call(number)),

_ => println!("У нас нет номера Даниеля."),

}

// `HashMap::insert()` вернёт `None`, если мы добавляем

// новое значение, иначе - `Some(value)`

contacts.insert("Даниель", "164-6743");

match contacts.get(&"Эшли") {

Some(&number) => println!("Звоним Эшли: {}", call(number)),

_ => println!("У нас нет номера Эшли."),

}

contacts.remove(&"Эшли");

// `HashMap::iter()` возвращает итератор, который в произвольном

// порядке отдаёт пары `(&'a key, &'a value)`.

for (contact, &number) in contacts.iter() {

println!("Звоним {}: {}", contact, call(number));

}

}

הההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההההה

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Для большей информации о том, как работает хеширование и хэш-карты (который иногда называются хэш-таблицами), вы можете обратиться к Wikipedia.

Альтернативные (пользовательские) типы ключей

Любой тип, реализующий типажи Eq и Hash могут являться ключами в HashMap. Туда входят:

   • bool (хотя он будет не очень полезен, так как будет всего лишь два возможных ключа)

   • int, uint и все их варианты

   • String и &str (подсказка: вы можете сделать HashMap с ключами типа String, а вызывать .get() - с &str)

Заметьте, что f32 и f64 не реализуют Hash, из-за того, что ошибки точности при работе с плавающей запятой могут привести к ужасным ошибкам при использовании их в качестве ключей для хэш-карт.

Все классы коллекций реализуют Eq и Hash если содержащийся в них тип также реализует Eq и Hash. Например, Vec реализует Hash, если T реализует Hash.

Вы можете легко реализовать Eq и Hash для пользовательских типов добавив всего лишь одну строчку: #[derive(PartialEq, Eq, Hash)]

Компилятор сделает всё остальное. Если вы хотите больше контроля над деталями, вы можете сами реализовать Eq и/или Hash. Данное руководство не охватывает специфику реализации Hash.

Чтобы поиграть с использованием struct в HashMap, давайте попробуем реализовать очень простую систему авторизации пользователей:

use std::collections::HashMap;

// `Eq` требует, чтобы для типа был также выведен `PartialEq`.

#[derive(PartialEq, Eq, Hash)]

struct Account<'a>{

username: &'a str,

password: &'a str,

}

struct AccountInfo<'a>{

name: &'a str,

email: &'a str,

}

type Accounts<'a> = HashMap, AccountInfo<'a>>;

fn try_logon<'a>(accounts: &Accounts<'a>,

username: &'a str, password: &'a str){

println!("Имя пользователя: {}", username);

println!("Пароль: {}", password);

println!("Попытка входа...");

let logon = Account {

username,

password,

};

match accounts.get(&logon) {

Some(account_info) => {

println!("Успешный вход!");

println!("Имя: {}", account_info.name);

println!("Email: {}", account_info.email);

},

_ => println!("Ошибка входа!"),

}

}

fn main(){

let mut accounts: Accounts = HashMap::new();

let account = Account {

username: "j.everyman",

password: "password123",

};

let account_info = AccountInfo {

name: "John Everyman",

email: "j.everyman@email.com",

};

accounts.insert(account, account_info);

try_logon(&accounts, "j.everyman", "psasword123");

try_logon(&accounts, "j.everyman", "password123");

}