Question
I am reading the documentation for File and saw this code:
use std::fs::File;
let mut file = File::create("foo.txt")?;
What does the ? operator mean in this line? I do not remember seeing it explained earlier in The Rust Book.
Short Answer
By the end of this page, you will understand what the ? operator does in Rust, why it is used with fallible operations like file handling, and how it helps return errors early without writing repetitive match statements. You will also see how it works with Result and Option, where it can be used, and what mistakes beginners commonly make.
Concept
The ? operator in Rust is a shortcut for error propagation.
Some operations can fail. For example:
- opening a file
- parsing a number
- reading user input
- making a network request
In Rust, these operations usually return a Result<T, E>:
Ok(value)means successErr(error)means failure
File::create("foo.txt") does not directly return a File. It returns something like:
Result<File, std::io::Error>
When you write:
let mut file = File::create("foo.txt")?;
Rust does this:
- if the result is
Ok(file), it extracts thefile - if the result is
Err(error), it immediately returns that error from the current function
Mental Model
Think of ? as a checkpoint on a path.
- If everything is fine, you pass through and keep walking.
- If there is a problem, you stop immediately and report it back.
For example, imagine a delivery process:
- Create the package label
- Put the item in the box
- Send the package
If step 1 fails, there is no point continuing. The ? operator says:
"If this step fails, stop the whole process and return the failure."
In Rust code, that means you do not manually check success and failure every time. ? does that for you in a safe and readable way.
Syntax and Examples
Basic syntax
let value = some_fallible_operation()?;
This usually means some_fallible_operation() returns a Result or Option.
Example with Result
use std::fs::File;
use std::io;
fn create_file() -> io::Result<File> {
let file = File::create("foo.txt")?;
Ok(file)
}
What this means
File::create("foo.txt")returnsio::Result<File>?unwraps theFileif successful- if it fails, the function returns the
io::Error Ok(file)returns the successful result
Step by Step Execution
Consider this function:
use std::fs::File;
use std::io::{self, Read};
fn load_text() -> io::Result<String> {
let mut file = File::open("notes.txt")?;
let mut text = String::new();
file.read_to_string(&mut text)?;
Ok(text)
}
Step-by-step
Step 1
Rust calls:
File::open("notes.txt")
This returns either:
Ok(file)if the file exists and can be openedErr(error)if it cannot be opened
Step 2
The ? operator checks the result.
Real World Use Cases
The ? operator is used everywhere in Rust code that deals with operations that may fail.
File handling
let data = std::fs::read_to_string("config.toml")?;
Useful when loading configuration files, templates, or saved data.
Parsing input
let port: u16 = input.parse()?;
Useful when converting strings into numbers, dates, or structured values.
Network and API calls
A request may fail because of a timeout, invalid response, or connection issue. ? makes these failure paths clean and readable.
Database operations
Queries may fail due to missing records, schema issues, or connection problems.
Command-line tools
CLI programs often read files, parse arguments, and write output. Many of these steps return Result, so ? is a natural fit.
Data processing pipelines
If one step fails, the whole operation may need to stop and return the error. ? expresses this clearly.
Real Codebase Usage
In real projects, developers use ? heavily to keep error-handling code compact.
Common pattern: validate, then continue
fn process(path: &str) -> std::io::Result<String> {
let text = std::fs::read_to_string(path)?;
Ok(text.trim().to_string())
}
Chaining fallible steps
use std::fs::File;
use std::io::{self, Read};
fn load() -> io::Result<String> {
let mut file = File::open("app.txt")?;
let mut text = String::new();
file.read_to_string(&mut text)?;
Ok(text)
}
Early returns without nested
Common Mistakes
1. Using ? in a function that does not return Result or Option
Broken code:
use std::fs::File;
fn make_file() {
let file = File::create("foo.txt")?;
}
Why it fails:
?may need to return early- but this function returns
() - Rust does not know how to return the error
Fix:
use std::fs::File;
use std::io;
fn make_file() -> io::Result<()> {
let _file = File::create("foo.txt")?;
Ok(())
}
2. Forgetting to return Ok(...) at the end
Broken code:
Comparisons
| Concept | What it does | On failure | Typical use |
|---|---|---|---|
? | Extracts success value and propagates failure | Returns early from the function | Normal error handling |
match | Handles every case manually | Whatever code you write | Full control |
unwrap() | Extracts success value | Panics | Quick experiments, tests |
expect("message") | Extracts success value | Panics with custom message | Debugging, assumptions |
? vs match
Cheat Sheet
Quick rules
?works onResultandOption- on success, it extracts the inner value
- on failure, it returns early from the current function
- the surrounding function must return a compatible type
Basic pattern
let value = fallible_operation()?;
With Result
fn run() -> Result<T, E> {
let value = something()?;
Ok(value)
}
With Option
fn run() -> Option<T> {
let value = maybe_value()?;
Some(value)
}
Equivalent to
FAQ
What does ? do in Rust?
It unwraps a successful Result or Option. If there is an error or missing value, it returns early from the function.
Is ? the same as unwrap()?
No. unwrap() panics on failure. ? returns the failure to the caller.
Can I use ? anywhere in Rust?
No. You can only use it where the current function, method, or closure returns a compatible type such as Result or Option.
Why does File::create("foo.txt")? work?
Because File::create returns a Result<File, io::Error>. The ? extracts the File on success or returns the io::Error on failure.
Does ? hide the error?
No. It passes the error upward so another part of the program can handle it.
Mini Project
Description
Build a small Rust program that reads a username from a file and returns it as a string. This project demonstrates how the ? operator simplifies error handling when multiple steps can fail, such as opening a file and reading its contents.
Goal
Create a function that loads text from a file using ? and returns either the contents or an I/O error.
Requirements
- Create a function that returns
std::io::Result<String>. - Open a text file from disk.
- Read the file contents into a
String. - Use the
?operator for each fallible operation. - Print the contents in
mainor print the error if loading fails.
Keep learning
Related questions
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Default Function Arguments in Rust: What to Use Instead
Learn how Rust handles default function arguments, why they are not supported, and practical patterns to achieve similar behavior.
Fixing Rust "linker 'cc' not found" on Debian in WSL
Learn why Rust shows "linker 'cc' not found" on Debian in WSL and how to fix it by installing the required C build tools.