1
0
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:
Martin Geisler
2022-12-21 16:36:30 +01:00
commit c212a473ba
252 changed files with 8047 additions and 0 deletions

View 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:?}");
}
```

View 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}"),
}
}
```

View 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:?}"),
}
}
```

View 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`.

View 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.

View 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}");
}
}
}
```

View 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:?}");
}
```