Collections

Strings

In Rust, Strings are implemented as a collection of bytes. There are effectively two string types: str, which is in the core language and commonly referred to as a string slice, and String, which is provided by Rust's standard library. Here are various ways to initialize a string:

let mut s = String::new(); // initializes an empty String
let data = "initial contents"; // is a string slice
let s = data.to_string(); // assign it to s, but first make it a String
let hi = "hi".to_string(); // or you could do this
let bye = String::from("bye"); // or this!

A String can grow in size and the contents can change. Let's look at some code that covers a variety of ways to alter a String:

main.rs
fn main() {
let mut foo = String::from("foo");
let bar = "bar";
foo.push_str(&bar);
println!("foo is {}", foo);
println!("bar is {}", bar);
let mut lol = String::from("lo");
lol.push('l');
println!("lol is {}", lol);
let hello = String::from("Hello, ");
let world = String::from("world!");
let hello_world = hello + &world; // Note that hello has been moved here and can no longer be used
println!("hello_world is {}", hello_world);
let tic = String::from("tic");
let tac = String::from("tac");
let toe = String::from("toe");
// this code is unweildy and it also moves `tic`.
// if you want to see what the error looks like,
// uncomment out line 23
// let game = tic + "-" + &tac + "-" + &toe;
let game = format!("{}-{}-{}", tic, tac, toe);
println!("game is {}", game)
}

The output of this should look like:

foo is foobar
bar is bar
lol is lol
hello_world is Hello, world!
game is tic-tac-toe

We can also iterate over a string using a for loop:

main.rs
fn main() {
for c in "hi y'all".chars() {
println!("{}", c);
}
}

Hash Maps

A hash map is a collection of key-value pairs. The type HashMap<K, V> stores a mapping of keys of type K to values of type V. Here's an example that creates a HashMap and retrieves values:

use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let score = get_score(scores, String::from("Red"));
//let score = get_score(scores, String::from("Blue"));
}
fn get_score(scores: HashMap<String, i64>, team_name: String) -> String {
let score = match scores.get(&team_name) {
Some(num) => num.to_string(),
None => "nonexistent".to_string()
};
println!("{}'s score is {}", team_name, score);
score
}

get will return an option type. To get the value out of the option (which has both Some<V> a None), we have to use pattern matching again. Since None doesn't have a value, I want to return the string "nonexistent" if the team isn't in the HashMap.

Why is let score = get_score(scores, String::from("Blue")); commented out? When we use scores in line 9, that makes the contents of scores owned by get_scores and you can no longer reference it. I'll be explaining a bit more about that in Ownership.