Lecture 9 - Loops
Follow-up from Friday
- Your papers are up here
- HW2 is due Wednesday. Please get started soon so we have time to help if you need it!
Office hours updates
Ava - Tuesday 3:45-5:45, 15th floor CDS
Prof Wheelock - Wednesday 2:30-4, 1506 in CDS
Joey - 3:30-5:30 Thursday, 15th floor CDS
Pratik - 4:00-6:00 on Fridays, 15th floor CDS
Clarifying "one expression per scope"
fn classify_grade(score: f64) -> char { if score > 90.0 { 'A' } else if score > 80.0 { 'B' } else if score > 70.0 { 'C' } else { 'D' } } fn main(){ println!("{}",classify_grade(5.5)); }
A complete solution to Friday's exercise
fn calculate_final_price(sticker_price: f64, tax_rate: f64, has_membership: bool) -> f64 { // Handle edge cases if sticker_price < 0.0 { println!("Warning: Negative price detected!"); } if tax_rate < 0.0 || tax_rate > 1.0 { println!("Warning: Unusual tax rate: {:.2}", tax_rate); } let final_price = if has_membership { sticker_price * (1.0 + tax_rate) * 0.9 } else { sticker_price * (1.0 + tax_rate) }; println!("Final Price is ${:.2}", final_price); final_price }
A few ways to write the core of the function (without printing)
#![allow(unused)] fn main() { fn calculate_final_price(sticker_price: f64, tax_rate: f64, has_membership: bool) -> f64 { if has_membership { sticker_price * (1.0 + tax_rate) * 0.9 } else { sticker_price * (1.0 + tax_rate) } } }
#![allow(unused)] fn main() { fn calculate_final_price(sticker_price: f64, tax_rate: f64, has_membership: bool) -> f64 { let mut final_price = sticker_price * (1.0 + tax_rate) if has_membership { final_price *= 0.9; }; final_price } }
#![allow(unused)] fn main() { fn calculate_final_price(sticker_price: f64, tax_rate: f64, has_membership: bool) -> f64 { let membership_discount = if has_membership { 0.9 } else { 1.0 }; sticker_price * (1.0 + tax_rate) * membership_discount } }
Learning Objectives
- Use
while,for,loop,break, andcontinueto build flexible loops - Use
breakto return values from loops andcontinueto skip iterations - Use
forloops with ranges (..and..=), array iteration, and enumerate - Create and manipulate fixed-size arrays with indexing, sorting, and length operations
- Apply loop labeling to control nested loop behavior
- Choose appropriate loop types based on use case requirements
for loops and ranges
Usage: loop over a range or collection
A range is (start..end), e.g. (1..5), where the index will vary as
$$ \textrm{start} \leq \textrm{index} < \textrm{end}. $$
Unless you use the notation (start..=end), in which case the index will vary as
$$ \textrm{start} \leq \textrm{index} \leq \textrm{end} $$
#![allow(unused)] fn main() { for i in (1..5) { println!("{}",i); }; }
More ways to play with ranges
#![allow(unused)] fn main() { for i in (1..5).rev() { // reverse order println!("{}",i) }; }
#![allow(unused)] fn main() { for i in (1..=5) { // inclusive range println!("{}",i); }; }
#![allow(unused)] fn main() { println!("This is a test"); for i in (1..5).step_by(2) { // every other element println!("{}",i); }; }
#![allow(unused)] fn main() { println!("And now for the reverse"); for i in (1..5).step_by(2).rev() { println!("{}",i) }; }
I suggest always trying out / printing what you're looping over during early development to make sure it's doing what you want it to do!
Arrays in Rust
- Arrays in Rust are of fixed length (we'll learn about more flexible
Veclater) - All elements of the same type (unlike tuples)
- You cannot add or remove elements from an array (but you can change its value)
What will this return?
#![allow(unused)] fn main() { let mut arr = [1,7,2,5,2]; arr[1] = 13; println!("{} {}",arr[0],arr[1]); println!("{}",arr.len()); }
for loop over an array
#![allow(unused)] fn main() { let mut arr = [1,7,2,5,2]; for x in arr { println!("{}",x); }; }
Quick tricks for making arrays
#![allow(unused)] fn main() { // create array of given length and fill it with a specific value // note the semi-colon vs the comma! let arr2 = [15;3]; for x in arr2 { print!("{} ",x); } println!(); }
#![allow(unused)] fn main() { // you can still infer or annotate types let arr2 : [u8;3] = [15;3]; }
Common Array Operations
Arrays come with useful built-in methods:
#![allow(unused)] fn main() { let mut scores = [85, 92, 78, 96, 88]; // Get the length println!("Number of scores: {}", scores.len()); // Sort the array (modifies the original array) scores.sort(); println!("Sorted scores: {:?}", scores); // Check if array contains a value println!("Contains 92? {}", scores.contains(&92)); // Note the & here! println!("Contains 100? {}", scores.contains(&100)); }
Note: {:?} is debug formatting - prints the entire array contents
Iterating with Indices
Sometimes you need both the position and the value:
#![allow(unused)] fn main() { let fruits = ["apple", "banana", "orange"]; // Method 1: Using enumerate() for (index, &fruit) in fruits.iter().enumerate() { // note the & here! println!("fruits[{}] = {}", index, fruit); } // Method 2: Using index range for i in 0..fruits.len() { println!("fruits[{}] = {}", i, fruits[i]); } }
When to use each:
- Use
enumerate()when you need both index and value - Stick to index range when you need to modify array elements
Lecture 8 Review Quiz
Take 2 minutes with a partner to review functions from last lecture:
-
What's wrong with this function signature?
#![allow(unused)] fn main() { fn calculate_area(width, height) -> f64 { } -
What's wrong with this function?
#![allow(unused)] fn main() { fn mystery(x: i32) -> i32 { let result = x * 2; result + 1; } } -
How can you fix this so it compiles?
#![allow(unused)] fn main() { let x = 4; let y = 4.5; let z = x + y; println!("{}",z); }
while loops
While loops continue as long as a condition remains true (very similar to python)
#![allow(unused)] fn main() { let mut number = 3; while number != 0 { println!("{number}!"); number -= 1; } println!("LIFTOFF!!!"); }
(What's a nice bit of style feedback here?)
Infinite loop
#![allow(unused)] fn main() { loop { // THIS WILL RUN OVER AND OVER FOREVER } }
- Similar to
while (True)in python - Need to use
breakto jump out of the loop!
A loop can return a value (break can act like return)
Just like the body of a function, a loop is an expression
To quit early, with or without a value:
- use
returnin a function - use
breakin a loop
#![allow(unused)] fn main() { let mut counter = 0; let final_count = loop { counter += 1; if counter > 100 { break counter*2; // Return twice the final counter value } }; println!("{}",final_count); }
You can also break out of for and while loops but can't get a value out of them. Can you guess why? (hint: because it's Rust, it's about safety...)
Using continue to jump to the next iteration
Think/pair/share - what is this going to print?
#![allow(unused)] fn main() { let mut x = 1; let result = loop { // you can capture a return value if x == 3 { x = x+1; continue; // skip the rest of this loop body and start the next iteration } println!("X is {}", x); x = x + 1; if x==6 { break x*2; // break with a return value } }; println!("Result is {}", result); }
Labeling loops to target with continue and break.
Labels let you use continue or break on any nested layer.
USE CAUTION - this can make your code hard to read and it's probably a red flag if you find yourself needing it often
fn main() { let mut count = 0; 'counting_up: loop { println!("count = {count}"); let mut remaining = 10; loop { println!("remaining = {remaining}"); if remaining == 9 { break; } if count == 2 { break 'counting_up; } remaining -= 1; } count += 1; } println!("End count = {count}"); }
Loop Selection Guidelines
For Loops:
- Iterating over ranges or collections
- When you need an index of what loop you're on
While Loops:
- Continue until some condition changes
- Don't know at the start how many times to loop
Loop (Infinite):
- Breaking on complex conditions (too much to include in
while) - Breaking at different places under different conditions