The Rust `?` operator keeps the successful value of a `Result` or returns its error from the enclosing function. It replaces a common `match` over `Ok` and `Err` without changing which function must handle the failure. section 5.2
Parse before calculating
In `fn square(val: &str) -> Result<i32, ParseIntError>`, `let num = val.parse::<i32>()?;` makes `num` available for squaring when parsing succeeds. For `"INVALID"`, `square` returns the parse error instead. The caller receives `Ok(4)` for `"2"` and an `Err` for invalid input. section 5.2
The enclosing function’s error type matters. Adding file operations to `square` introduces `std::io::Error`, which cannot simply be returned as its declared `ParseIntError`. Use a suitable return error type and convert errors where needed. In the web-service database path, `From<SQLxError> for EzyTutorError` supplies the conversion used by `?`; `map_err` is another way to choose the error before propagating it. section 5.2 section 5.4 section 6.3.3
Listing 5.4 Error handling in the method to retrieve courses for a tutor
pub async fn get_courses_for_tutor_db(
pool: &PgPool,
tutor_id: i32,
) -> Result<Vec<Course>, EzyTutorError> { #1
// Prepare SQL statement
let course_rows = sqlx::query!(
"SELECT tutor_id, course_id, course_name,
posted_time FROM ezy_course_c5 where tutor_id = $1",
tutor_id
)
.fetch_all(pool)
.await?; #2
// Extract result
let courses: Vec<Course> = course_rows
.iter()
.map(|course_row| Course {
course_id: course_row.course_id,
tutor_id: course_row.tutor_id,
course_name: course_row.course_name.clone(),
posted_time: Some(chrono::NaiveDateTime::from(
course_row.posted_time.unwrap())),
})
.collect();
match courses.len() { #3
0 => Err(EzyTutorError::NotFound(
"Courses not found for tutor".into(),
)),
_ => Ok(courses),
}
}
Follow `.fetch_all(pool).await?` in the course retrieval function: a successful fetch proceeds to row mapping, while a fetch error returns through its `Result<Vec<Course>, EzyTutorError>` signature instead of panicking at the fetch. section 5.4
A route handler can then decide how to respond. This separates database failure from HTTP response construction: the course handler maps successful results to JSON, while the custom error type supports error responses. The same propagation pattern appears outside handlers when server startup uses `.bind(addr)?` in a `main` function returning `std::io::Result<()>`. section 5.4 section 7.2



