Control Flow
Control flow determines the order in which code executes. In this section, we’ll explore Rust’s control flow structures and compare them to JavaScript’s.
Conditional Statements
Section titled “Conditional Statements”if Expressions
Section titled “if Expressions”In JavaScript, if
statements work like this:
// JavaScript if statement
let number = 5;
if (number > 0) {
console.log("Positive");
} else if (number < 0) {
console.log("Negative");
} else {
console.log("Zero");
}
In Rust, if
is an expression (returns a value), not just a statement:
// Rust if expression
let number = 5;
if number > 0 {
println!("Positive");
} else if number < 0 {
println!("Negative");
} else {
println!("Zero");
}
Key differences:
- No parentheses
()
around conditions (though they’re optional) - Condition must be a boolean expression (no truthy/falsy values)
- Braces
{}
are required, even for single-line blocks
if as an Expression
Section titled “if as an Expression”Since if
is an expression in Rust, you can assign its result to a variable:
let number = 5;
let sign = if number > 0 {
"positive"
} else if number < 0 {
"negative"
} else {
"zero"
};
println!("The number is {}", sign);
This is similar to the ternary operator in JavaScript:
const number = 5;
const sign = number > 0 ? "positive" : number < 0 ? "negative" : "zero";
console.log(`The number is ${sign}`);
Truthiness vs. Boolean Conditions
Section titled “Truthiness vs. Boolean Conditions”JavaScript has the concept of truthy and falsy values:
if (1) {
console.log("1 is truthy");
}
if (0) {
// This won't execute
} else {
console.log("0 is falsy");
}
if ("hello") {
console.log("Non-empty strings are truthy");
}
Rust only accepts boolean conditions:
if true {
println!("This will execute");
}
// This won't compile
// if 1 {
// println!("Rust doesn't have truthy values");
// }
// You must use an explicit comparison
if 1 > 0 {
println!("This will execute");
}
while Loops
Section titled “while Loops”While loops in Rust and JavaScript are very similar:
// Rust while loop
let mut count = 0;
while count < 5 {
println!("Count: {}", count);
count += 1;
}
// JavaScript while loop
let count = 0;
while (count < 5) {
console.log(`Count: ${count}`);
count += 1;
}
The main differences:
- No parentheses around the condition in Rust (though they’re optional)
- Braces are required in Rust
for Loops
Section titled “for Loops”For loops are quite different between the two languages:
JavaScript’s C-style for loop:
for (let i = 0; i < 5; i++) {
console.log(`i: ${i}`);
}
Rust’s for-in loop:
for i in 0..5 {
println!("i: {}", i);
}
The 0..5
is a range that includes 0, 1, 2, 3, and 4 (but not 5).
JavaScript for-of loop:
const numbers = [10, 20, 30, 40, 50];
for (const num of numbers) {
console.log(`Number: ${num}`);
}
Rust for-in loop with an array:
let numbers = [10, 20, 30, 40, 50];
for num in numbers.iter() {
println!("Number: {}", num);
}
Or with references:
let numbers = [10, 20, 30, 40, 50];
for num in &numbers {
println!("Number: {}", num);
}
Iterating with indices
Section titled “Iterating with indices”If you need indices along with values:
JavaScript:
const letters = ["a", "b", "c"];
for (let i = 0; i < letters.length; i++) {
console.log(`Index ${i}: ${letters[i]}`);
}
// Or using forEach with index
letters.forEach((letter, index) => {
console.log(`Index ${index}: ${letter}`);
});
Rust:
let letters = ["a", "b", "c"];
for (i, &letter) in letters.iter().enumerate() {
println!("Index {}: {}", i, letter);
}
loop: Rust’s Infinite Loop
Section titled “loop: Rust’s Infinite Loop”Rust has a loop
keyword for infinite loops:
// Rust infinite loop
let mut counter = 0;
loop {
counter += 1;
println!("Count: {}", counter);
if counter >= 10 {
break;
}
}
In JavaScript, you’d use while (true)
:
// JavaScript infinite loop
let counter = 0;
while (true) {
counter += 1;
console.log(`Count: ${counter}`);
if (counter >= 10) {
break;
}
}
Returning Values from Loops
Section titled “Returning Values from Loops”In Rust, you can return a value from a loop
:
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
println!("Result: {}", result); // 20
In JavaScript, you’d need to assign the value outside the loop:
let counter = 0;
let result;
while (true) {
counter += 1;
if (counter == 10) {
result = counter * 2;
break;
}
}
console.log(`Result: ${result}`); // 20
Loop Labels
Section titled “Loop Labels”Rust supports loop labels to break or continue outer loops:
'outer: for x in 0..5 {
for y in 0..5 {
if x * y > 10 {
println!("Breaking at x={}, y={}", x, y);
break 'outer;
}
}
}
JavaScript also supports labeled statements, though they’re less commonly used:
outer: for (let x = 0; x < 5; x++) {
for (let y = 0; y < 5; y++) {
if (x * y > 10) {
console.log(`Breaking at x=${x}, y=${y}`);
break outer;
}
}
}
Pattern Matching with match
Section titled “Pattern Matching with match”One of Rust’s most powerful features is the match
expression:
enum Coin {
Penny,
Nickel,
Dime,
Quarter,
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => 1,
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter => 25,
}
}
It’s similar to JavaScript’s switch
, but more powerful:
function valueInCents(coin) {
switch (coin) {
case "penny":
return 1;
case "nickel":
return 5;
case "dime":
return 10;
case "quarter":
return 25;
default:
throw new Error("Unknown coin");
}
}
Key differences:
match
is an expression that returns a valuematch
ensures exhaustiveness (all possibilities must be covered)match
supports complex pattern matching
Pattern Matching with Ranges
Section titled “Pattern Matching with Ranges”let number = 7;
match number {
1 => println!("One"),
2 | 3 => println!("Two or three"),
4..=10 => println!("Between four and ten, inclusive"),
_ => println!("Something else"),
}
In JavaScript, you’d need multiple case statements:
const number = 7;
switch (number) {
case 1:
console.log("One");
break;
case 2:
case 3:
console.log("Two or three");
break;
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
console.log("Between four and ten, inclusive");
break;
default:
console.log("Something else");
}
Matching with Option<T>
Section titled “Matching with Option<T>”match
is often used with Option<T>
to handle the presence or absence of a value:
fn find_even(numbers: &[i32]) -> Option<i32> {
for &num in numbers {
if num % 2 == 0 {
return Some(num);
}
}
None
}
let numbers = [1, 3, 5, 7, 9];
match find_even(&numbers) {
Some(n) => println!("Found an even number: {}", n),
None => println!("No even numbers found"),
}
In JavaScript, you’d check for null
or undefined
:
function findEven(numbers) {
for (const num of numbers) {
if (num % 2 === 0) {
return num;
}
}
return null;
}
const numbers = [1, 3, 5, 7, 9];
const result = findEven(numbers);
if (result !== null) {
console.log(`Found an even number: ${result}`);
} else {
console.log("No even numbers found");
}
if let Syntax
Section titled “if let Syntax”For simpler pattern matching, Rust offers if let
syntax:
let some_value = Some(3);
// Using match
match some_value {
Some(3) => println!("Three!"),
_ => (),
}
// Using if let (more concise)
if let Some(3) = some_value {
println!("Three!");
}
In JavaScript, you might use destructuring assignment:
const someValue = { value: 3 };
// Similar to if let
if (someValue?.value === 3) {
console.log("Three!");
}
Key Differences from JavaScript
Section titled “Key Differences from JavaScript”- Expression-based: Rust’s
if
andmatch
are expressions that return values - No truthy/falsy values: Rust requires explicit boolean conditions
- Pattern matching: Rust’s
match
is more powerful than JavaScript’sswitch
- Loop syntax: Rust’s
for
loops are more like JavaScript’sfor...of
loops - Loop labels: Both languages support loop labels, but they’re more common in Rust
- Exhaustiveness checking: Rust ensures you handle all possibilities in a
match
Next Steps
Section titled “Next Steps”Now that you’ve learned about control flow in Rust, let’s explore Ownership, one of Rust’s most distinctive features that doesn’t exist in JavaScript.