Enums with data in Rust - Time & Space Complexity
When using enums with data in Rust, it's important to understand how the program's work grows as the input changes.
We want to see how the time to handle enum data changes when we process many enum values.
Analyze the time complexity of the following code snippet.
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn process_messages(messages: &[Message]) {
for msg in messages {
match msg {
Message::Quit => println!("Quit message"),
Message::Move { x, y } => println!("Move to {}, {}", x, y),
Message::Write(text) => println!("Text message: {}", text),
Message::ChangeColor(r, g, b) => println!("Change color to {}, {}, {}", r, g, b),
}
}
}
This code defines an enum with different data types and processes a list of such messages one by one.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Looping through each message in the list.
- How many times: Once for each message in the input slice.
As the number of messages grows, the program processes each message once.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | 10 message checks and prints |
| 100 | 100 message checks and prints |
| 1000 | 1000 message checks and prints |
Pattern observation: The work grows directly with the number of messages.
Time Complexity: O(n)
This means the time to process messages grows in a straight line as the number of messages increases.
[X] Wrong: "Matching on enums with data takes more time for complex data types like strings, so it grows faster than the number of messages."
[OK] Correct: Matching just checks the variant type quickly; the time mainly depends on how many messages you process, not the data size inside each enum variant.
Understanding how enum processing scales helps you explain how your code handles different data efficiently, a useful skill in many coding tasks.
"What if we added nested loops inside the match arms that process data inside each enum variant? How would the time complexity change?"
Practice
Solution
Step 1: Understand what enums with data do
Enums with data allow each variant to hold different types or amounts of data, grouping related but different information.Step 2: Compare options with this understanding
Only To group related values that can be different types under one name correctly describes grouping related values of different types under one name.Final Answer:
To group related values that can be different types under one name -> Option AQuick Check:
Enums with data = group different types [OK]
- Thinking enums only store one data type
- Confusing enums with arrays or vectors
- Assuming enums perform calculations
Solution
Step 1: Recall enum variant syntax with data
In Rust, enum variants with data are defined by listing the variant name followed by parentheses containing the data types.Step 2: Check each option
enum Color { Red(u8, u8, u8), Blue } uses correct syntax: Red(u8, u8, u8). Options A, B, and C use invalid syntax with ->, =, or : which are incorrect for enum variant data.Final Answer:
enum Color { Red(u8, u8, u8), Blue } -> Option AQuick Check:
Variant with data uses parentheses () [OK]
- Using = instead of () for variant data
- Using : or -> which are invalid here
- Forgetting parentheses for data
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
}
fn main() {
let msg = Message::Move { x: 10, y: 20 };
match msg {
Message::Quit => println!("Quit"),
Message::Move { x, y } => println!("Move to {}, {}", x, y),
Message::Write(text) => println!("Write {}", text),
}
}Solution
Step 1: Identify the enum variant used
The variable msg is assigned Message::Move with x=10 and y=20.Step 2: Match arms and output
The match statement checks msg. For Message::Move, it prints "Move to {}, {}" with x and y values, so output is "Move to 10, 20".Final Answer:
Move to 10, 20 -> Option DQuick Check:
Match Message::Move prints coordinates [OK]
- Confusing variant names
- Expecting Quit or Write output
- Thinking code won't compile
enum Shape {
Circle(f64),
Rectangle { width: f64, height: f64 },
}
fn area(shape: Shape) -> f64 {
match shape {
Shape::Circle(radius) => 3.14 * radius * radius,
Shape::Rectangle(width, height) => width * height,
}
}Solution
Step 1: Check enum variant definitions
Circle has tuple data (f64), Rectangle has named fields { width, height }.Step 2: Check match patterns
Circle pattern is correct. Rectangle pattern uses (width, height) which is wrong; it should use { width, height } to match named fields.Final Answer:
Incorrect pattern for Rectangle variant in match -> Option CQuick Check:
Match named fields with braces {} [OK]
- Using parentheses for named fields
- Ignoring pattern mismatch errors
- Changing return type unnecessarily
enum Event {
Click { x: i32, y: i32 },
KeyPress(char),
Resize(u32, u32),
}
fn describe(event: Event) -> String {
match event {
Event::Click { x, y } if x > 0 && y > 0 => format!("Clicked at positive coords: {}, {}", x, y),
Event::Click { .. } => "Clicked at non-positive coords".to_string(),
Event::KeyPress(c) if c.is_alphabetic() => format!("Key pressed: {}", c),
Event::KeyPress(_) => "Non-alphabetic key pressed".to_string(),
Event::Resize(w, h) if w == h => "Resized to square".to_string(),
Event::Resize(_, _) => "Resized to rectangle".to_string(),
}
}
fn main() {
let e = Event::Resize(100, 100);
println!("{}", describe(e));
}Solution
Step 1: Identify the enum variant and values
The event is Event::Resize with width=100 and height=100.Step 2: Match arms with conditions
The Resize arm with condition w == h matches because 100 == 100, so it returns "Resized to square".Final Answer:
Resized to square -> Option BQuick Check:
Resize with equal sides = square [OK]
- Ignoring if conditions in match arms
- Confusing Resize with Click variant
- Assuming default arm without conditions
Ready to prove it?
Pass the test and this pattern moves unknown
