Rust by Example — страница 55 из 66

// `_x` should get destroyed at this point

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

Let's check that panic! doesn't leak memory.

$ 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

Where vectors store values by an integer index, HashMaps store values by key. HashMap keys can be booleans, integers, strings, or any other type that implements the Eq and Hash traits. More on this in the next section.

Like vectors, HashMaps are growable, but HashMaps can also shrink themselves when they have excess space. You can create a HashMap with a certain starting capacity using HashMap::with_capacity(uint), or use HashMap::new() to get a HashMap with a default initial capacity (recommended).

use std::collections::HashMap;

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

match number {

"798-1364" => "We're sorry, the call cannot be completed as dialed.

Please hang up and try again.",

"645-7689" => "Hello, this is Mr. Awesome's Pizza. My name is Fred.

What can I get for you today?",

_ => "Hi! Who is this again?"

}

}

fn main() {

let mut contacts = HashMap::new();

contacts.insert("Daniel", "798-1364");

contacts.insert("Ashley", "645-7689");

contacts.insert("Katie", "435-8291");

contacts.insert("Robert", "956-1745");

// Takes a reference and returns Option<&V>

match contacts.get(&"Daniel") {

Some(&number) => println!("Calling Daniel: {}", call(number)),

_ => println!("Don't have Daniel's number."),

}

// `HashMap::insert()` returns `None`

// if the inserted value is new, `Some(value)` otherwise

contacts.insert("Daniel", "164-6743");

match contacts.get(&"Ashley") {

Some(&number) => println!("Calling Ashley: {}", call(number)),

_ => println!("Don't have Ashley's number."),

}

contacts.remove(&"Ashley");

// `HashMap::iter()` returns an iterator that yields

// (&'a key, &'a value) pairs in arbitrary order.

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

println!("Calling {}: {}", contact, call(number));

}

}

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

XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

For more information on how hashing and hash maps (sometimes called hash tables) work, have a look at Hash Table Wikipedia

Alternate/custom key types

Any type that implements the Eq and Hash traits can be a key in HashMap. This includes:

   • bool (though not very useful since there is only two possible keys)

   • int, uint, and all variations thereof

   • String and &str (protip: you can have a HashMap keyed by String and call .get() with an &str)

Note that f32 and f64 do not implement Hash, likely because floating-point precision errors would make using them as hashmap keys horribly error-prone.

All collection classes implement Eq and Hash if their contained type also respectively implements Eq and Hash. For example, Vec will implement Hash if T implements Hash.

You can easily implement Eq and Hash for a custom type with just one line: #[derive(PartialEq, Eq, Hash)]

The compiler will do the rest. If you want more control over the details, you can implement Eq and/or Hash yourself. This guide will not cover the specifics of implementing Hash.

To play around with using a struct in HashMap, let's try making a very simple user logon system:

use std::collections::HashMap;

// Eq requires that you derive PartialEq on the type.

#[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: {}", username);

println!("Password: {}", password);

println!("Attempting logon...");

let logon = Account {

username,

password,

};

match accounts.get(&logon) {

Some(account_info) => {

println!("Successful logon!");

println!("Name: {}", account_info.name);

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

},

_ => println!("Login failed!"),

}

}

fn main(){

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

let account = Account {

username: "j.everyman",

password: "password123",