Custom error types in Rust - Time & Space Complexity
When we create custom error types in Rust, we want to know how the time to create or handle these errors changes as our program grows.
We ask: How does the cost of working with custom errors grow when the program or input size increases?
Analyze the time complexity of the following Rust code defining and using a custom error type.
#[derive(Debug)]
enum MyError {
NotFound(String),
PermissionDenied,
}
fn check_access(user: &str) -> Result<(), MyError> {
if user == "admin" {
Ok(())
} else {
Err(MyError::NotFound(user.to_string()))
}
}
This code defines a custom error type and a function that returns an error or success based on input.
Look for operations that repeat or grow with input size.
- Primary operation: Creating or returning the custom error value.
- How many times: Once per function call, no loops or recursion here.
The time to create or return the error depends on the size of data inside the error, like the string in NotFound.
| Input Size (length of string) | Approx. Operations |
|---|---|
| 10 | About 10 steps to copy/store the string |
| 100 | About 100 steps |
| 1000 | About 1000 steps |
Pattern observation: The time grows roughly in direct proportion to the size of the string stored in the error.
Time Complexity: O(n)
This means the time to create or handle the error grows linearly with the size of the data inside the error.
[X] Wrong: "Creating a custom error always takes constant time no matter what."
[OK] Correct: If the error holds data like strings, the time depends on how big that data is, so it can grow with input size.
Understanding how error creation time grows helps you write efficient Rust code and explain your choices clearly in interviews.
What if the custom error stored a fixed-size number instead of a string? How would the time complexity change?
Practice
Solution
Step 1: Understand the role of custom errors
Custom error types help programmers describe specific problems clearly in their code.Step 2: Compare with other options
Custom errors do not speed up code, fix bugs automatically, or replaceResulttypes; they provide clear error descriptions.Final Answer:
To describe specific problems in your program with clear error messages. -> Option DQuick Check:
Custom error purpose = clear problem description [OK]
- Thinking custom errors fix bugs automatically
- Confusing custom errors with performance improvements
- Believing custom errors replace Result types
Solution
Step 1: Identify Rust enum syntax
Rust usesenumto define custom error types with variants, likeIo(std::io::Error).Step 2: Check other options
structis for data structures,fnis for functions, andclassis not Rust syntax.Final Answer:
enum MyError { Io(std::io::Error), Parse(std::num::ParseIntError) } -> Option AQuick Check:
Custom error enum = enum with variants [OK]
- Using struct instead of enum for error variants
- Trying to define errors as functions
- Using class keyword which is invalid in Rust
use std::fmt;
#[derive(Debug)]
enum MyError {
NotFound,
PermissionDenied,
}
impl fmt::Display for MyError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MyError::NotFound => write!(f, "Item not found"),
MyError::PermissionDenied => write!(f, "Permission denied"),
}
}
}
fn main() {
let err = MyError::NotFound;
println!("Error: {}", err);
}Solution
Step 1: Understand Display implementation
Thefmtmethod matchesMyError::NotFoundand writes "Item not found" to the formatter.Step 2: Check main output
Printingerrwith{}usesDisplay, so output is "Error: Item not found".Final Answer:
Error: Item not found -> Option BQuick Check:
Display prints custom message = "Item not found" [OK]
- Expecting Debug output instead of Display
- Confusing variant name with message
- Assuming compilation error without Display trait
enum MyError {
Io(std::io::Error),
}
impl std::fmt::Display for MyError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}Solution
Step 1: Check how self.0 is accessed
In an enum, you cannot directly accessself.0without matching or destructuring the variant.Step 2: Understand correct pattern
You must matchselfto get the innerstd::io::Errorvalue before using it inwrite!.Final Answer:
Cannot access self.0 because enum variant is not destructured. -> Option AQuick Check:
Enum variant fields need matching before access [OK]
- Trying to access enum fields directly without match
- Thinking Display can't be implemented for enums
- Assuming Debug trait is mandatory for Display
Solution
Step 1: Choose enum for multiple error types
An enum cleanly groups different error variants like IO and parse errors.Step 2: Implement Display and std::error::Error traits
Display provides friendly messages; implementing std::error::Error integrates with Rust error handling.Final Answer:
Define an enum with variants for each error type, implement Display with match arms for messages, and implement std::error::Error. -> Option CQuick Check:
Enum + Display + Error trait = best custom error pattern [OK]
- Using struct with optional fields causing confusion
- Skipping std::error::Error trait implementation
- Using panic! instead of proper error handling
Ready to prove it?
Pass the test and this pattern moves unknown
