forked from rust-lang/rust-by-example
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
HashMap, keys, HashSet, SmallIntMap. Closes rust-lang#161
- Loading branch information
Showing
9 changed files
with
302 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
use std::collections::HashMap; | ||
|
||
// Eq requires that you derive PartialEq on the type. | ||
#[deriving(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<Account<'a>, 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: username, | ||
password: password, | ||
}; | ||
|
||
match accounts.find(&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", | ||
}; | ||
|
||
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"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
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 | ||
(see `SmallIntMap` for a more streamlined map implementation keyed by `uint`) | ||
|
||
Note that `f32` and `f64` do *not* implement `Hash`, | ||
likely because floating-point precision errors | ||
would make using them as hash map keys very frustrating. | ||
|
||
All collection classes implement `Eq`, and implicitly implement `Hash` | ||
if their contained type also implements `Hash`. | ||
E.g. `Vec<T>` will implement `Hash` if `T` implements `Hash`. | ||
|
||
You can easily implement `Eq` and `Hash` for a custom type with just one line: | ||
`#[deriving(Eq,Hash)]` | ||
|
||
The compiler will do the rest. If you want more control over the details, | ||
you can implement `Eq` and `Hash` yourself. | ||
This guide will not cover the specifics of implementing `Hash`. | ||
|
||
Note that two instances of a type that are equal | ||
(such that `a == b` returns `true`) should hash to the same value, | ||
but two instances with the same hash don't necessarily have to be equal. | ||
Read up on [hashes][hash] and [hash collisions][collision] | ||
for more information. | ||
|
||
To play around with using a `struct` in `HashMap`, | ||
let's try making a very simple user logon system: | ||
|
||
{alt-key-types.play} | ||
|
||
[hash]: http://en.wikipedia.org/wiki/Hash_function | ||
[collision]: http://en.wikipedia.org/wiki/Hash_collision |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
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.find(&("Daniel")) { | ||
Some(&number) => println!("Calling Daniel: {}", call(number)), | ||
_ => println!("Don't have Daniel's number."), | ||
} | ||
|
||
// `HashMap::insert()` returns true | ||
// if the inserted value is new, false otherwise | ||
contacts.insert("Daniel", "164-6743"); | ||
|
||
match contacts.find(&("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)); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
use std::collections::HashSet; | ||
|
||
fn main() { | ||
let mut a: HashSet<int> = vec!(1i, 2, 3).move_iter().collect(); | ||
let mut b: HashSet<int> = vec!(2i, 3, 4).move_iter().collect(); | ||
|
||
assert!(a.insert(4)); | ||
assert!(a.contains(&4)); | ||
|
||
// `HashSet::insert()` returns false if | ||
// there was a value already present. | ||
assert!(b.insert(4), "Value 4 is already in set B!"); | ||
// FIXME ^ Comment out this line | ||
|
||
b.insert(5); | ||
|
||
// If a collection's element type implements `Show`, | ||
// then the collection implements `Show`. | ||
// It usually prints its elements in the format `[elem1, elem2, ...]` | ||
println!("A: {}", a); | ||
println!("B: {}", b); | ||
|
||
// Print [1, 2, 3, 4, 5] in arbitrary order | ||
println!("Union: {}", a.union(&b).collect::<Vec<&int>>()); | ||
|
||
// This should print [1] | ||
println!("Difference: {}", a.difference(&b).collect::<Vec<&int>>()); | ||
|
||
// Print [2, 3, 4] in arbitrary order. | ||
println!("Intersection: {}", a.intersection(&b).collect::<Vec<&int>>()); | ||
|
||
// Print [1, 5] | ||
println!("Symmetric Difference: {}", | ||
a.symmetric_difference(&b).collect::<Vec<&int>>()); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
Consider a `HashSet` as a `HashMap` where the key and value are the same. | ||
Or, to be more precise, a `HashMap` where we just care about the keys. | ||
|
||
"What's the point of that?" you ask. "I could just store the keys in a `Vec`." | ||
|
||
A `HashSet`'s unique feature is that | ||
it is guaranteed to not have duplicate elements. | ||
That's the contract that any [`Set`][set] implementation fulfills. | ||
`HashSet` is just one implementation. (see also: [`MutableSet`][mutableset]) | ||
|
||
If you insert a value that is already present in the `HashSet`, | ||
(i.e. the new value is equal to the existing and they both have the same hash), | ||
then the new value will replace the old. | ||
|
||
This is great for when you never want more than one of something, | ||
or when you want to know if you've already got something. | ||
|
||
But sets can do more than that. | ||
If they weren't important, then why would there be a | ||
[branch of mathematics][set-theory] dedicated to them? | ||
|
||
Sets have 4 primary operations (all of the following calls return an iterator): | ||
|
||
* `union`: get all the elements in one set or the other. | ||
|
||
* `difference`: get all the elements that are in the first set but not the second. | ||
|
||
* `intersection`: get all the elements that are only in *both* sets. | ||
|
||
* `symmetric_difference`: | ||
get all the elements that are in one set or the other, but *not* both. | ||
|
||
Try all of these in the following example. | ||
|
||
{hashset.play} | ||
|
||
Sets don't seem so pointless now, do they? | ||
|
||
(Examples adapted from the [documentation.][hash-set]) | ||
|
||
[set]: http://doc.rust-lang.org/std/collections/trait.Set.html | ||
[mutableset]: http://doc.rust-lang.org/std/collections/trait.MutableSet.html | ||
[set-theory]: http://en.wikipedia.org/wiki/Set_theory | ||
[hash-set]: http://doc.rust-lang.org/std/collections/hashmap/struct.HashSet.html#method.difference |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
Where vectors store values by an integer index, `HashMap`s 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, `HashMap`s 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). | ||
|
||
{hash.play} | ||
|
||
For more information on how hashing and hash maps | ||
(sometimes called hash tables) work, have a look at | ||
[Wikipedia][wiki-hash] | ||
|
||
[wiki-hash]: (http://en.wikipedia.org/wiki/Hash_table) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
`SmallIntMap` can be very useful in certain cases. | ||
You can use it anywhere you would use a `HashMap<uint, _>`. | ||
Internally, it's just a wrapper for `Vec` | ||
that handles growth and inconsistent indexes. | ||
|
||
Because it uses a `Vec` for storage, | ||
`SmallIntMap` will need to grow to the size of its largest key. | ||
So if your largest key is 50, | ||
a `SmallIntMap` will take up as much space as 50 elements. | ||
Thus the emphasis on *small*. | ||
|
||
Here's a good example: | ||
a program for managing a small apartment complex. | ||
Some apartments might be occupied, some might be empty. | ||
Maybe you'll add more later, | ||
and you want the program to be able to adapt without any extra work. | ||
|
||
{smallintmap.play} | ||
|
||
You might not normally use `SmallIntMap`, | ||
but maybe, one day, you'll have a need for it. | ||
And when you do need it, it will be there. And you'll know how to use it. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
use std::collections::SmallIntMap; | ||
|
||
struct Tenant<'a> { | ||
name: &'a str, | ||
phone: &'a str, | ||
} | ||
|
||
fn main() { | ||
// Start with 5 apartments | ||
let mut apartments = SmallIntMap::with_capacity(5); | ||
|
||
// The compiler infers 1 as uint | ||
apartments.insert(1, Tenant { | ||
name: "John Smith", | ||
phone: "555-1234", | ||
}); | ||
|
||
apartments.insert(3, Tenant { | ||
name: "Henrietta George", | ||
phone: "555-2314", | ||
}); | ||
|
||
apartments.insert(5, Tenant { | ||
name: "David Rogers", | ||
phone: "555-5467", | ||
}); | ||
|
||
apartments.pop(&1); | ||
match apartments.find_mut(&3) { | ||
Some(henrietta) => henrietta.name = "David and Henrietta Smith", | ||
_ => println!("Oh no! Where did David and Henrietta go?"), | ||
} | ||
|
||
apartments.insert(0, Tenant { | ||
name: "Phillip Davis", | ||
phone: "5555-7869", | ||
}); | ||
|
||
for (key, tenant) in apartments.iter(){ | ||
println!("{}: {} ({})", key, tenant.name, tenant.phone); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters