Error Handling in Rust: try/catch and Beyond

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: The Philosophy of Error Handling in Rust



Unlike many languages with exceptions (Java, Python, C++), Rust does not use the classic try/catch construct. Instead, the language offers a more explicit and type-safe approach. Errors in Rust are not hidden control flows, but return values. This makes code predictable and protects against "silent" failures.



Rust has two main types for representing error situations:

  • Result<T, E> — for recoverable errors (I/O errors, parsing errors, business logic errors).
  • Option<T> — for the absence of a value (an analogue of null, but safe).


In this article, we will break down how to work with these types, what to replace them with from the world of try/catch, and how to write reliable code in Rust.



1. Result<T, E>: The Main Tool for Error Handling



The Result type is an enum with two variants: Ok(T) (success) and Err(E) (error). It is used in most functions that might fail.



1.1. Creating and Returning a Result



use std::fs::File;use std::io::Read;

fn read_file(path: &str) -> Result<String, std::io::Error> { let mut file = File::open(path)?; // the ? operator — analogue of try! let mut contents = String::new(); file.read_to_string(&mut contents)?; Ok(contents)}


Here, the ? operator is a key element. It works like try in other languages: if the result is Err, the function immediately returns that error. If Ok, it extracts the value.



1.2. Handling with match



The most explicit way is to use match:



let result = read_file("config.txt");

match result { Ok(content) => println!("File read: {}", content), Err(e) => eprintln!("Error: {}", e),}


This is an analogue of try/catch, but more flexible and controlled.



1.3. Helper Methods



  • .unwrap() — panics on error (only for prototypes).
  • .expect(msg) — panics with a custom message.
  • .unwrap_or(default) — returns a default value.
  • .is_ok() / .is_err() — state checking.


2. Option<T>: Safe Handling of Absent Values



Option is another enum with variants Some(T) and None. It replaces null and nullptr, eliminating null pointer dereference errors.



2.1. Usage Example



fn find_user(id: u32) -> Option<String> {    if id == 1 {        Some("Alice".to_string())    } else {        None    }}

let user = find_user(2);println!("User: {}", user.unwrap_or("Guest".to_string()));


2.2. Combining with Result



Often Option is converted to Result for uniform handling:



fn get_user_name(id: u32) -> Result<String, String> {    find_user(id).ok_or("User not found".to_string())}


The .ok_or() method turns Some into Ok, and None into Err.



3. The ? Operator: An Elegant Replacement for try/catch



The ? operator is syntactic sugar that automatically unwraps Result or Option. It is only available in functions that return the corresponding type.



3.1. Example with a Call Chain



use std::num::ParseIntError;

fn parse_and_add(a: &str, b: &str) -> Result<i32, ParseIntError> { let x: i32 = a.parse()?; let y: i32 = b.parse()?; Ok(x

Blogs

Book Recommendations