Collections
Vector
Vectors allow you to store multiple values next to each other in memory. Vectors can only contain a single type. Enums of the same type count, so you can store multiple kinds of values using an enum. You can create vectors with the new
class function, or with the vec!
macro.
let v: Vec<i32> = Vec::new();
let v = vec![1, 2, 3];
v.push(5);
v.push(6);
v.push(7);
v.push(8);
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
Values can be read from a vector with brackets or the get
method. Retrieving from a vector gives you a reference to the element. If you try to access a vector with brackets that is out of the range, then the code will panic.
let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2];
let third: Option<&i32> = v.get(2);
To iterate over a vector, we use the for in
syntax. You can also iterate over a mutable vector and mutate the elements.
let v = vec![100, 32, 57];
for i in &v {
println!("{}", i);
}
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;
}
Strings
String
The String
type is part of the Rust standard library instead of imbedded in the core language. It is owned, mutable, growable, and UTF-8 encoded. A String
is actually just a wrapper of the Vec<T>
type, so a lot of the same methods apply. You can append to a mutable string with push_str
which takes a string slice, so it does not take ownership of the item being passed in.
let mut s = String::new();
let s2 = "bar";
s1.push_str(s2);
println!("s2 is {}", s2);
// From string literal
let data = "initial contents";
let s = data.to_string();
let s = "initial contents".to_string();
let s = String::from("initial contents");
Concatenating strings can be done with either the +
operator or the format!
macro. You can see that s2
is still usable after the concatenation, this is because the add function has the definition fn add(self, s: &str) -> String
, meaning the second argument is a string slice and therefore not transfered (&String
can be transformed to a &str
automatically). format!
takes strings and returns another String
and doesn't take ownership of any of the arguments.
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; // note s1 has been moved here and can no longer be used
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{}-{}-{}", s1, s2, s3);
You can iterate over strings by using a for in
loop and the chars()
function.
for c in "З21".chars() {
println!("{}", c);
}
&str
or String Slice
A string slice is a reference to a part of a String
. When writing functions that take a string reference, it is better to use &str
because &String
is automatically converted to a string slice if passed into a function that takes one.
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
Hash Maps
Stores a collection of key-value pairs. The keys must all be the same type, and the values must all be the same type. The get
method returns an Option<&V>
. For types that implement the Copy
trait, they will be copied when inserted into the hash map. Otherwise, like strings, ownership will be transfered. If you insert a reference, the value the reference is pointing to must be valid for the lifetime of the hash map.
use std::collections::HashMap;
let mut scores = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
let team_name = String::from("Blue");
let score = scores.get(&team_name);
for (key, value) in &scores {
println!("{}: {}", key, value);
}
scores.insert(String::from("Blue"), 25); // Overwrites
scores.entry(String::from("Blue")).or_insert(50); // Add if not present
// Update value based on previous value
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
let count = map.entry(word).or_insert(0);
*count += 1;
}