You've already forked comprehensive-rust
mirror of
https://github.com/google/comprehensive-rust.git
synced 2025-06-17 06:37:34 +02:00
Publish Comprehensive Rust 🦀
This commit is contained in:
51
src/error-handling/converting-error-types.md
Normal file
51
src/error-handling/converting-error-types.md
Normal file
@ -0,0 +1,51 @@
|
||||
# Converting Error Types
|
||||
|
||||
The actual expansion of `?` is a little more complicated than previously indicated:
|
||||
|
||||
```rust,ignore
|
||||
expression?
|
||||
```
|
||||
|
||||
actually becomes
|
||||
|
||||
```rust,ignore
|
||||
match expression {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Err(From::from(err)),
|
||||
}
|
||||
```
|
||||
|
||||
The `From::from` call here means we attempt to convert the error type to the
|
||||
type returned by the function:
|
||||
|
||||
```rust,editable
|
||||
use std::{fs, io};
|
||||
use std::io::Read;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum ReadUsernameError {
|
||||
IoError(io::Error),
|
||||
EmptyUsername(String),
|
||||
}
|
||||
|
||||
impl From<io::Error> for ReadUsernameError {
|
||||
fn from(err: io::Error) -> ReadUsernameError {
|
||||
ReadUsernameError::IoError(err)
|
||||
}
|
||||
}
|
||||
|
||||
fn read_username(path: &str) -> Result<String, ReadUsernameError> {
|
||||
let mut username = String::with_capacity(100);
|
||||
fs::File::open(path)?.read_to_string(&mut username)?;
|
||||
if username.is_empty() {
|
||||
return Err(ReadUsernameError::EmptyUsername(String::from(path)));
|
||||
}
|
||||
Ok(username)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
//fs::write("config.dat", "").unwrap();
|
||||
let username = read_username("config.dat");
|
||||
println!("username: {username:?}");
|
||||
}
|
||||
```
|
35
src/error-handling/deriving-error-enums.md
Normal file
35
src/error-handling/deriving-error-enums.md
Normal file
@ -0,0 +1,35 @@
|
||||
# Deriving Error Enums
|
||||
|
||||
The [thiserror](https://docs.rs/thiserror/) crate is a popular way to crate an
|
||||
error enum like we did on the previous page:
|
||||
|
||||
```rust,editable,compile_fail
|
||||
use std::{fs, io};
|
||||
use std::io::Read;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum ReadUsernameError {
|
||||
#[error("Could not read: {0}")]
|
||||
IoError(#[from] io::Error),
|
||||
#[error("Found no username in {0}")]
|
||||
EmptyUsername(String),
|
||||
}
|
||||
|
||||
fn read_username(path: &str) -> Result<String, ReadUsernameError> {
|
||||
let mut username = String::with_capacity(100);
|
||||
fs::File::open(path)?.read_to_string(&mut username)?;
|
||||
if username.is_empty() {
|
||||
return Err(ReadUsernameError::EmptyUsername(String::from(path)));
|
||||
}
|
||||
Ok(username)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
//fs::write("config.dat", "").unwrap();
|
||||
match read_username("config.dat") {
|
||||
Ok(username) => println!("Username: {username}"),
|
||||
Err(err) => println!("Error: {err}"),
|
||||
}
|
||||
}
|
||||
```
|
39
src/error-handling/error-contexts.md
Normal file
39
src/error-handling/error-contexts.md
Normal file
@ -0,0 +1,39 @@
|
||||
# Adding Context to Errors
|
||||
|
||||
The widely used [anyhow](https://docs.rs/anyhow/) crate can help you add
|
||||
contextual information to your errors:
|
||||
|
||||
```rust,editable,compile_fail
|
||||
use std::{fs, io};
|
||||
use std::io::Read;
|
||||
use thiserror::Error;
|
||||
use anyhow::{Context, Result};
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
enum ReadUsernameError {
|
||||
#[error("Could not read: {0}")]
|
||||
IoError(#[from] io::Error),
|
||||
#[error("Found no username in {0}")]
|
||||
EmptyUsername(String),
|
||||
}
|
||||
|
||||
fn read_username(path: &str) -> Result<String> {
|
||||
let mut username = String::with_capacity(100);
|
||||
fs::File::open(path)
|
||||
.context(format!("Failed to open {path}"))?
|
||||
.read_to_string(&mut username)
|
||||
.context("Failed to read")?;
|
||||
if username.is_empty() {
|
||||
return Err(ReadUsernameError::EmptyUsername(String::from(path)))?;
|
||||
}
|
||||
Ok(username)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
//fs::write("config.dat", "").unwrap();
|
||||
match read_username("config.dat") {
|
||||
Ok(username) => println!("Username: {username}"),
|
||||
Err(err) => println!("Error: {err:?}"),
|
||||
}
|
||||
}
|
||||
```
|
21
src/error-handling/panic-unwind.md
Normal file
21
src/error-handling/panic-unwind.md
Normal file
@ -0,0 +1,21 @@
|
||||
# Catching the Stack Unwinding
|
||||
|
||||
By default, a panic will cause the stack to unwind. The unwinding can be caught:
|
||||
|
||||
```rust
|
||||
use std::panic;
|
||||
|
||||
let result = panic::catch_unwind(|| {
|
||||
println!("hello!");
|
||||
});
|
||||
assert!(result.is_ok());
|
||||
|
||||
let result = panic::catch_unwind(|| {
|
||||
panic!("oh no!");
|
||||
});
|
||||
assert!(result.is_err());
|
||||
```
|
||||
|
||||
* This can be useful in servers which should keep running even if a single
|
||||
request crashes.
|
||||
* This does not work if `panic = abort` is set in your `Cargo.toml`.
|
14
src/error-handling/panics.md
Normal file
14
src/error-handling/panics.md
Normal file
@ -0,0 +1,14 @@
|
||||
# Panics
|
||||
|
||||
Rust will trigger a panic if a fatal error happens at runtime:
|
||||
|
||||
```rust,editable,should_panic
|
||||
fn main() {
|
||||
let v = vec![10, 20, 30];
|
||||
println!("v[100]: {}", v[100]);
|
||||
}
|
||||
```
|
||||
|
||||
* Panics are for unrecoverable and unexpected errors.
|
||||
* Panics are symptoms of bugs in the program.
|
||||
* Use non-panicking APIs (such as `Vec::get`) if crashing is not acceptable.
|
23
src/error-handling/result.md
Normal file
23
src/error-handling/result.md
Normal file
@ -0,0 +1,23 @@
|
||||
# Structured Error Handling with `Result`
|
||||
|
||||
We have already see the `Result` enum. This is used pervasively when errors are
|
||||
expected as part of normal operation:
|
||||
|
||||
```rust
|
||||
use std::fs::File;
|
||||
use std::io::Read;
|
||||
|
||||
fn main() {
|
||||
let file = File::open("diary.txt");
|
||||
match file {
|
||||
Ok(mut file) => {
|
||||
let mut contents = String::new();
|
||||
file.read_to_string(&mut contents);
|
||||
println!("Dear diary: {contents}");
|
||||
},
|
||||
Err(err) => {
|
||||
println!("The diary could not be opened: {err}");
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
46
src/error-handling/try-operator.md
Normal file
46
src/error-handling/try-operator.md
Normal file
@ -0,0 +1,46 @@
|
||||
# Propagating Errors with `?`
|
||||
|
||||
The try-operator `?` is used to return errors to the caller. It lets you turn
|
||||
the common
|
||||
|
||||
```rust,ignore
|
||||
match some_expression {
|
||||
Ok(value) => value,
|
||||
Err(err) => return Err(err),
|
||||
}
|
||||
```
|
||||
|
||||
into the much simpler
|
||||
|
||||
```rust,ignore
|
||||
some_expression?
|
||||
```
|
||||
|
||||
We can use this to simplify our error handing code:
|
||||
|
||||
```rust,editable
|
||||
use std::fs;
|
||||
use std::io::{self, Read};
|
||||
|
||||
fn read_username(path: &str) -> Result<String, io::Error> {
|
||||
let username_file_result = fs::File::open(path);
|
||||
|
||||
let mut username_file = match username_file_result {
|
||||
Ok(file) => file,
|
||||
Err(e) => return Err(e),
|
||||
};
|
||||
|
||||
let mut username = String::new();
|
||||
|
||||
match username_file.read_to_string(&mut username) {
|
||||
Ok(_) => Ok(username),
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
//fs::write("config.dat", "alice").unwrap();
|
||||
let username = read_username("config.dat");
|
||||
println!("username: {username:?}");
|
||||
}
|
||||
```
|
Reference in New Issue
Block a user