My Rust Journey - 5th November 2024
This is the 7th post about my journey learning the Rust programming language using the Rust Book. Previous posts include:
Chapter 1: Basics of Rust and Cargo
Chapter 4: Ownership, reference and borrowing, and slice type
Chapter 5: Ownership, reference and borrowing, and slice type
Chapter 6: Enums, Control Flow and Matching
Chapter 7: Packages, Crates and Modules
I am documenting this as I think it is a useful thing to do for people interested in learning Rust from my non-developer perspective.
At this stage, you have already installed Rust on your machine, and you are ready to write and run your first Rust programs.
I am using VS code with the rust-analyzer extension. I am working on an M1-mac.
The following tutorial will cover Chapter 8 of the Rust Book. It is meant to be a summary and used with the book as a complementary source of information.
Vectors
See below ways to create a vector.
let v: Vec<i32> = Vec::new(); //creating empty vector
let mut v = Vec::new();
v.push(5); //pushing values into a vector
v.push(6);
v.push(7);
v.push(8);
let v = vec![1, 2, 3, 4, 5]; //create a vector with vec! macro
We can create a vector using Vec<T>
or the macro vec!
. The first time we specify v
we need to specify the type as well because we are creating an empty vector and Rust cannot know what is in it at compile time. If we create v
and then push numbers into it, Rust can infer and we do not to specify the type when we create the empty vector. At compile time v
will contain some values.
We can extract the third value in v
by taking a reference to it: &v[2]
.
We can also do some additional operations. If we use the get
method we end up with an Option<T>
that we can use with match
.
let third: Option<&i32> = v.get(2);
match third {
Some(third) => println!("The third element is {third}."),
None => println!("There is no element."),
}
This will allow our program not to enter panic if we specify and index that is outside our vector.
We can also iterate through a vector using for
loops.
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;
println!("{i}");
}
In this case, we make the vector mutable so that when we iterate through it we can do some operations to modify its elements. Not how to change the value that the mutable reference refers to we need to dereference with the *
operator.
We can define an enum to store values of different types into one vector.
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![ //within the main function
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
The enum specifies the types we can have in a spreadsheet cell. The vector constructs the row with different types.
Storing UTF-8 Encoded text with Strings
We can specify a string literal and then specify the String
type using the to_string
method or the usual String::from()
.
let data = "foo";
let s = data.to_string();
let mut s = String::from("foo");
We can update a string using the push_str
method or the push
method for char literals.
s.push_str("bar");
println!("{s}");
s.push('!');
println!("{s}");
We can also combine different strings:
let s1 = String::from("Hello, ");
let s2 = String::from("world!");
let s3 = s1 + &s2; //s1 moved to s3
The +
operator uses the add
method that takes a reference to a string &str
as input. This is why we used &s2
. But &s2
is &String
type. The add
method uses a deref coercion turning &s2
into &s2[..]
. Because the method does not take ownership of the input parameter, s2
is still valid after the operation.
For concatenating multiple strings we can use the format
macro.
let s1 = String::from("tic");
let s2 = String::from("tac");
let s3 = String::from("toe");
let s = format!("{s1}-{s2}-{s3}");
Iterating over strings
You can iterate over strings using the bytes
or chars
methods.
for b in "hello".chars() {
println!("{b}");
}
For more information about indexing strings see the Rust book.
Hash maps
Hash maps are the last type of collection for this chapter (after vectors and strings). Like vectors, hash maps store data on the heap memory and are homogeneous (key and values must have the same type).
Hash maps take the form HashMap<K, V>
where K
are the keys and V
the values.
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).copied().unwrap_or(0);
The score
has the value associated with the βBlueβ key. The get
method returns an Option<&i32>
, but copied
actually returns Option<>i32
. The unwrap_or
sets the score to 0 if no key exists. We can iterate through the Hash map as follows:
for (key, value) in &scores {
println!("{key}: {value}");
}
Once values are inserted into a hash map, they are owned by it. Values with Copy trait are copied into it, but owned values are moved into it.
Each unique key can have one associated value at a time.
We can modify hashmaps as follows:
scores.insert(String::from("Blue"), 25); // overwriting
scores.entry(String::from("Yellow")).or_insert(200); //adding a key and value if not present
scores.entry(String::from("Blue")).or_insert(100);
The entry
method takes the key we want to check as a parameter and gives back an enum called Entry
that represents a value that might or might not exist. The or_insert
method on Entry
returns a mutable reference to the value of the corresponding key if it exists, or it attaches a new key with the new value. In this case, both keys already exist, and the code wonβt do anything.
The code below counts the occurrence of words in a sentence.
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;
}
println!("{:?}", map);
The split_whitespace
method returns an iterator over subslices separated by space. At each iteration, the loop adds word
and inserts 0 if there is no value, adding +1. If a word occurs more than once, it just adds +1.
In this chapter, we learned about common collections in Rust. See you in the next post!
π₯ Web3 explained from the non-developer's POV. π Helping Polkadot users explore the ecosystem with confidence. I post daily on socials. Opinions are mine.
0 comments