🦀Tutorial: Rust Part 2

Welcome to the second tutorial in our Rust series! This guide builds on the foundational knowledge from Tutorial 1, diving into more topics that are central to writing robust, efficient, and idiomatic Rust code.
Work through the exercises below. Each question includes a sample solution and an explanation in the associated video.
Question 1. Basic Loops: while
Section titled “Question 1. Basic Loops: while”Rust has several ways to loop. The while loop is perfect for when you want a loop to run as long as a condition is true.
- Create a mutable variable
counterand set it to 5. - Write a
whileloop that continues as long as the counter is not equal to 0. - Inside the loop, print the value of the counter and then subtract 1 from it.
- After the loop finishes, print “Liftoff!”.
When you are finished, change the code to use a for instead. Note: you can use the .rev() function to reverse a range.
Question 2. String vs. String Slices (&str)
Section titled “Question 2. String vs. String Slices (&str)”Understanding the difference between the String type and a string slice (&str) is important in Rust.
String: An owned, heap-allocated, growable text type.&str: An immutable view or slice into a string. This is a reference.
Task:
- In
main, create an ownedStringnamedmy_stringwith the value “Hello, Rust!”. - Create a function
print_slicethat takes one argument: a string slice namedsof type&str. - The
print_slicefunction should just print the slice it receives. - From
main, callprint_sliceand pass it a slice ofmy_string. - Pass an arbitrary string literal — e.g.,
"Some String"(i.e.,&'static str). - Pass a substring slice — e.g.,
&my_string[0..5].
Try rewriting print_slice to take String instead of &str. Which calls still work, and why might the compiler complain?
Question 3. Loops with Iterators (for loop)
Section titled “Question 3. Loops with Iterators (for loop)”(a) The most common and idiomatic way to loop in Rust is with a for loop, which uses iterators.
- Create a
Vec(vector) or an array namedanimalscontaining three strings: “Dog”, “Cat”, and “Mouse”. - Write a
forloop to iterate over theanimalsvector. - Inside the loop, print a message for each animal, such as “I love my Pet: [animal]”.
(b) We can modify this code using .enumerate() to show how to access the index and value. Adapt your code to display the index and value.
(c) Finally, modify the code to use .iter_mut() that lets you modify the elements to reset all of the animals in the vector to be “unknown”. Display all the elements once again.
Question 4. Pattern Matching with enum
Section titled “Question 4. Pattern Matching with enum”Enums (enumerations) let you define a type with a few possible variants. The match statement is the perfect way to handle each variant.
- Define an
enumcalledTrafficLightwith three variants:Red,Yellow, andGreen. - Create a function
get_actionthat takes aTrafficLight(by reference) and returns a&'static str(a static string slice). - Inside
get_action, use amatchstatement to check theTrafficLightvariant:Redshould return “Stop!”Yellowshould return “Caution!”Greenshould return “Go!”
- In
main, create aTrafficLightand print the action for it.
Question 5. struct and impl
Section titled “Question 5. struct and impl”A struct (structure) lets you group related data together. An impl (implementation) block is where you define methods associated with that struct.
- Define a
structnamedStudentwith the following fields:name: Stringstudent_id: u32grade: f32
- Create an
implblock forStudent. - Inside the
implblock, create an associated function (like a constructor) callednewthat takes a name, ID, and grade, and returns a newStudentinstance. - Inside the
implblock, create a methodhas_honorsthat takes&self(a reference to the instance) and returns abool(true ifgradeis greater than 40, false otherwise). - In
main, create a new student usingStudent::new()and then print whether or not they have honors by calling thehas_honorsmethod.
Question 6. Enums and Pattern Matching
Section titled “Question 6. Enums and Pattern Matching”Enums (enumerations) allow you to define a type by enumerating its possible variants. They are incredibly powerful when combined with the match control flow operator for pattern matching.
- Define an
enumcalledWebEventwith the following variants:PageLoadKeyPress(char)Click { x: i32, y: i32 }
- Create a function
inspect_eventthat takes aWebEventand prints a different message for each variant. - In
main, create aVec<WebEvent>containing at least one of each variant and loop through it, callinginspect_eventfor each event.
Question 7. Error Handling with Result<T, E>
Section titled “Question 7. Error Handling with Result<T, E>”Rust handles errors by returning a Result<T, E> enum, which has two variants: Ok(T) for success and Err(E) for failure. This makes error handling explicit and robust.
- Write a function
parse_numberthat takes a string slice (&str) and tries to parse it into ani32. - The function should return
Ok(i32)if parsing is successful andErr(String)with an error message if it fails. - In
main, call this function with both a valid number string and an invalid one, and use amatchstatement to print either the successful result or the error.
Question 8. Collections: HashMap<K, V>
Section titled “Question 8. Collections: HashMap<K, V>”The HashMap<K, V> collection stores key-value pairs. It’s a common and useful data structure for lookups.
- Create a mutable
HashMapto store the scores of two teams, “Blue” and “Red”. - Insert initial scores: Blue starts with 10 and Red starts with 50. Use the
HashMapinsert()function. - Use
entryandor_insertto add 100 points to the “Blue” team’s score. These are combined in theform scores.entry(...).or_insert(0);where the entry() call returns anEntryenum and the.or_insert(0)call on this enum returns a mutable reference to the existing value. If the key does not exist it inserts the value provided, which is0and gives you a mutable reference to that. - Loop through the
HashMapand print each team’s name and score.
Question 9. Generic Functions
Section titled “Question 9. Generic Functions”Generics allow you to write code that operates on abstract data types, avoiding code duplication. We are going to write a simple function to get the largest value in a slice — for example, the largest integer in a slice: vec![10, 20, 30, 40, 50, 35]
- Create a generic function
get_largest<T: PartialOrd>(list: &[T]) -> &Tthat finds the largest element in a slice of any type that implements thePartialOrdtrait. - In
main, call this function with both a slice of integers and a slice of characters and print the results.
Note: The PartialOrd trait in Rust is used to define partial ordering for types—meaning that some values can be compared (e.g. using <, >, <=, >=), but not necessarily all. It’s typically implemented alongside PartialEq, since ordering implies equality checks. Unlike Ord, which requires a total order where every pair of values is comparable, PartialOrd allows comparisons that may return None to indicate that two values cannot be meaningfully compared (as with floating-point NaN).
Question 10. Closures and Iterators
Section titled “Question 10. Closures and Iterators”Closures are anonymous functions you can save in a variable or pass as arguments to other functions. Iterators provide a sequence of values that can be manipulated using methods like map, filter, and collect.
The closure |&x| x * 2 is a short, inline anonymous function in Rust. Here’s what it means step by step:
- The part between the vertical bars
|&x|defines the parameter list — in this case, it takes a reference to an integer as its input. - The
&xmeans the closure expects to receive a reference, not a plain value. So if you pass this closure to something that iterates over references (likefor_eachon a vector using.iter()), it will match correctly. - Inside the closure body,
x * 2uses the dereferenced value ofx(since it was a reference) and doubles it. This is very similar to a lambda function we saw in Chapter 6 of the notes on C++.
Steps:
- Create a
Vec<i32>of numbers from 1 to 10. You will need to use the.collect()call on the range to turn a range into a collection ofVec<i32> - Use the
Debugtrait to print out the values in theVec<i32>. - Use an iterator, a closure, and the
mapmethod to create a new vector where each number is doubled. Use the closure explained above. - Use an iterator, a closure, and the
filtermethod to create another new vector containing only the even numbers from the original vector. You can use the modulo operator for this. You will also have toclone()the value into the new vector. - Print both new vectors.
Solutions
Section titled “Solutions”Here are the video solutions — please do not watch these solutions without having attempted the questions first. Please note that these solutions are somewhat warts and all in that I make errors and correct them live without edits.
Alternatively, you can view the video directly on YouTube: https://www.youtube.com/watch?v=DQ1lWDVXPzk
© 2026 Derek Molloy, Dublin City University. All rights reserved.