938 lines
41 KiB
Text
938 lines
41 KiB
Text
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Error handling"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Panic\n",
|
||
"\n",
|
||
"* A program panics when it encounters something so messed up that there must be a bug in the program itself. Something like:\n",
|
||
" * Out-of-bounds array access\n",
|
||
" * Integer division by zero\n",
|
||
" * Calling `.unwrap()` on an `Option` that happens to be `None`\n",
|
||
" * Assertion failure\n",
|
||
"* A panic is not a crash. It’s not undefined behavior. The behavior is well-defined!\n",
|
||
"* Macro `panic!` can trigger a panic directly"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"\n",
|
||
"thread '<unnamed>' panicked at src\\lib.rs:6:1:\n",
|
||
"Critical error!\n",
|
||
"stack backtrace:\n",
|
||
" 0: std::panicking::begin_panic_handler\n",
|
||
" at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\std\\src\\panicking.rs:697\n",
|
||
" 1: core::panicking::panic_fmt\n",
|
||
" at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\core\\src\\panicking.rs:75\n",
|
||
" 2: ctx::run_user_code_2::{{closure}}\n",
|
||
" 3: _rust_try.llvm.7135721178753807917\n",
|
||
" 4: std::panic::catch_unwind\n",
|
||
" 5: run_user_code_2\n",
|
||
" 6: <unknown>\n",
|
||
" 7: <unknown>\n",
|
||
" 8: <unknown>\n",
|
||
" 9: <unknown>\n",
|
||
" 10: <unknown>\n",
|
||
" 11: <unknown>\n",
|
||
" 12: <unknown>\n",
|
||
" 13: <unknown>\n",
|
||
" 14: BaseThreadInitThunk\n",
|
||
" 15: RtlUserThreadStart\n",
|
||
"note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"panic!(\"Critical error!\");"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Unwinding\n",
|
||
"\n",
|
||
"* Panic typically proceeds as follows:\n",
|
||
" * An error message is printed to the terminal. If you set the `RUST_BACKTRACE` environment variable, as the messages suggests, Rust will also dump the stack at this point.\n",
|
||
" * The stack is unwound. This is a lot like C++ exception handling. Any temporary values, local variables, or arguments that the current function was using are dropped, in the reverse of the order they were created. Dropping a value simply means cleaning up after it: any `String`s or `Vec`s the program was using are freed, any open `File`s are closed, and so on. User-defined `drop` methods are called too.\n",
|
||
" * Finally, the thread exits. If the panicking thread was the main thread, then the whole process exits (with a nonzero exit code)."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Aborting\n",
|
||
"\n",
|
||
"* Stack unwinding is the default panic behavior, but there are two circumstances in which Rust does not try to unwind the stack.\n",
|
||
" * If a `.drop()` method triggers a second panic while Rust is still trying to clean up after the first, this is considered fatal. Rust stops unwinding and aborts the whole process.\n",
|
||
" * Rust’s panic behavior is customizable. If you compile with `-C panic=abort`, the first panic in your program immediately aborts the process. "
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Result"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* A `Result` represents an operation that can either succeed (returning a result) or fail (returning an error).\n",
|
||
"* It is defined as:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"mod explain {\n",
|
||
" pub enum Result<T, E> {\n",
|
||
" Ok(T),\n",
|
||
" Err(E),\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"#[derive(Debug)]\n",
|
||
"struct ReadFileError {\n",
|
||
" message: String\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl std::fmt::Display for ReadFileError {\n",
|
||
" fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {\n",
|
||
" write!(f, \"ReadFileError: {}\", self.message)\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl std::error::Error for ReadFileError {}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"ename": "Error",
|
||
"evalue": "failed to resolve: use of unresolved module or unlinked crate `io`",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m[E0433] Error:\u001b[0m failed to resolve: use of unresolved module or unlinked crate `io`",
|
||
" \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_5:1:1 \u001b[38;5;246m]\u001b[0m",
|
||
" \u001b[38;5;246m│\u001b[0m",
|
||
" \u001b[38;5;246m2 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54mo\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m<\u001b[0m\u001b[38;5;249mS\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mh\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m use of unresolved module or unlinked crate `io`",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m│\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m help: a builtin type with a similar name exists: `i8`",
|
||
" \u001b[38;5;240m │\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;115mNote\u001b[0m: help: consider importing this module: `use std::io;",
|
||
" \u001b[38;5;240m │\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m `",
|
||
"\u001b[38;5;246m───╯\u001b[0m"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"fn read_file(path: &str) -> Result<String, ReadFileError> { \n",
|
||
" let file: io::Result<String> = std::fs::read_to_string(path);\n",
|
||
" \n",
|
||
" match file {\n",
|
||
" Ok(content) => Result::Ok(content),\n",
|
||
" Err(err) => Result::Err(ReadFileError{message: err.to_string()}),\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"ename": "Error",
|
||
"evalue": "cannot find function `read_file` in this scope",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m[E0425] Error:\u001b[0m cannot find function `read_file` in this scope",
|
||
" \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_6:1:1 \u001b[38;5;246m]\u001b[0m",
|
||
" \u001b[38;5;246m│\u001b[0m",
|
||
" \u001b[38;5;246m1 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;54m_\u001b[0m\u001b[38;5;54mf\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m not found in this scope",
|
||
"\u001b[38;5;246m───╯\u001b[0m"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"let file_content = read_file(\"file.txt\");\n",
|
||
"\n",
|
||
"match file_content {\n",
|
||
" Ok(content) => println!(\"{}\", content),\n",
|
||
" Err(err) => println!(\"{}\", err),\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Result methods"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {
|
||
"vscode": {
|
||
"languageId": "rust"
|
||
}
|
||
},
|
||
"source": [
|
||
"* `result.unwrap()` also returns the success value, if result is a success result. However, if result is an error result, this method panics.\n",
|
||
"* `result.expect(message)` is the same as `.unwrap()`, but lets you provide a message that it prints in case of panic.\n",
|
||
"* `result.is_ok()` and `result.is_err()` return a `bool` telling if result is a success result or an error result.\n",
|
||
"* `result.ok()` returns the success value, if any, as an `Option<T>`. If result is a success result, this returns `Some(success_value)`; otherwise, it returns `None`, discarding the error value.\n",
|
||
"* `result.err()` returns the error value, if any, as an `Option<E>`.\n",
|
||
"* `result.unwrap_or(fallback)` returns the success value, if result is a success result. Otherwise, it returns `fallback`, discarding the error value."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"ename": "Error",
|
||
"evalue": "cannot find function `read_file` in this scope",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m[E0425] Error:\u001b[0m cannot find function `read_file` in this scope",
|
||
" \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_7:1:1 \u001b[38;5;246m]\u001b[0m",
|
||
" \u001b[38;5;246m│\u001b[0m",
|
||
" \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mc\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54md\u001b[0m\u001b[38;5;54m_\u001b[0m\u001b[38;5;54mf\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54ml\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mk\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mx\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m\"\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mw\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249mp\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249mD\u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mF\u001b[0m\u001b[38;5;249mA\u001b[0m\u001b[38;5;249mU\u001b[0m\u001b[38;5;249mL\u001b[0m\u001b[38;5;249mT\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mF\u001b[0m\u001b[38;5;249mI\u001b[0m\u001b[38;5;249mL\u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mC\u001b[0m\u001b[38;5;249mO\u001b[0m\u001b[38;5;249mN\u001b[0m\u001b[38;5;249mT\u001b[0m\u001b[38;5;249mE\u001b[0m\u001b[38;5;249mN\u001b[0m\u001b[38;5;249mT\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mo\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mg\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m not found in this scope",
|
||
"\u001b[38;5;246m───╯\u001b[0m"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"static DEFAULT_FILE_CONTENT: &str = \"Default content\";\n",
|
||
"\n",
|
||
"let file_content = read_file(\"unknown_file.txt\").unwrap_or(DEFAULT_FILE_CONTENT.to_string());\n",
|
||
"\n",
|
||
"file_content"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* `result.unwrap_or_else(fallback_fn)` is the same, but instead of passing a fallback value directly, you pass a function or closure. This is for cases where it would be wasteful to compute a fallback value if you’re not going to use it. The `fallback_fn` is called only if we have an error result.\n",
|
||
"* `result.as_ref()` converts a `Result<T, E>` to a `Result<&T, &E>`, borrowing a reference to the success or error value in the existing result.\n",
|
||
"* `result.as_mut()` is the same, but borrows a mutable reference. The return type is `Result<&mut T, &mut E>`.\n",
|
||
"\n",
|
||
"#### Mapping\n",
|
||
"\n",
|
||
"* Mapping means transforming the success or error value inside a `Result`, while leaving the other value untouched. It simplifies code that would otherwise require matching on the `Result` and handling both cases.\n",
|
||
" * `result.map(success_fn)` transforms a `Result<T, E>` to a `Result<U, E>` by applying the function `success_fn` to the success value, if any. The error value is left untouched.\n",
|
||
" * `result.map_err(error_fn)` is the same, but applies `error_fn` to the error value, if any, transforming a `Result<T, E>` to a `Result<T, F>`."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 10,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"This is a file with some text.\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"()"
|
||
]
|
||
},
|
||
"execution_count": 10,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"fn read_file(path: &str) -> Result<String, ReadFileError> { \n",
|
||
" std::fs::read_to_string(path).map_err(|err| ReadFileError{message: err.to_string()})\n",
|
||
"}\n",
|
||
"\n",
|
||
"let file_content = read_file(\"file.txt\");\n",
|
||
"\n",
|
||
"match file_content {\n",
|
||
" Ok(content) => println!(\"{}\", content),\n",
|
||
" Err(err) => println!(\"{}\", err),\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Result Type Aliases\n",
|
||
"\n",
|
||
"* Modules often define a `Result` type alias to avoid having to repeat an error type that’s used consistently by almost every function in the module"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 11,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"Some(\"This is a file with some text.\")"
|
||
]
|
||
},
|
||
"execution_count": 11,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"mod custom {\n",
|
||
" pub type Result<T> = std::result::Result<T, ReadFileError>;\n",
|
||
"\n",
|
||
" #[derive(Debug)]\n",
|
||
" pub struct ReadFileError {\n",
|
||
" message: String\n",
|
||
" }\n",
|
||
"\n",
|
||
" pub fn read_file(path: &str) -> Result<String> { \n",
|
||
" let file = std::fs::read_to_string(path);\n",
|
||
" \n",
|
||
" match file {\n",
|
||
" Ok(content) => Result::Ok(content),\n",
|
||
" Err(err) => Result::Err(ReadFileError{message: err.to_string()}),\n",
|
||
" }\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"custom::read_file(\"file.txt\").ok()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Trait std::error::Error - Printing Errors"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* All of standard library errors have a common interface, the `std::error::Error` trait, which means they share the following features:\n",
|
||
" * They’re all printable using `println!()`. Printing an error with the `{}` format specifier typically displays only a brief error message. Alternatively, you can print with the `{:?}` format specifier, to get a `Debug` view of the error\n",
|
||
" * `err.description()` returns an error message as a `&str`.\n",
|
||
" * `err.source()` returns an `Option<&(dyn Error + 'static)>`: the underlying error, if any, that triggered `err`."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Propagating Error"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* In most places where we try something that could fail, we don’t want to catch and handle the error immediately\n",
|
||
"* Instead, if an error occurs, we usually want to let our caller deal with it. We want errors to propagate up the call stack.\n",
|
||
"* Rust has a `?` operator that does this. You can add a `?` to any expression that produces a `Result`, such as the result of a function call:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 12,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"use std::io;\n",
|
||
"\n",
|
||
"fn read_file_content(name: &str) -> io::Result<String> {\n",
|
||
" use std::fs::File;\n",
|
||
" use std::io::prelude::*;\n",
|
||
" \n",
|
||
" let mut file = File::open(name)?; \n",
|
||
" let mut contents = String::new();\n",
|
||
" file.read_to_string(&mut contents)?;\n",
|
||
" Ok(contents)\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 13,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Error: Os { code: 2, kind: NotFound, message: \"Nie można odnaleźć określonego pliku.\" }\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"()"
|
||
]
|
||
},
|
||
"execution_count": 13,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"let file_content = read_file_content(\"unknown_file.txt\");\n",
|
||
"\n",
|
||
"match file_content {\n",
|
||
" Ok(content) => println!(\"{}\", content),\n",
|
||
" Err(err) => println!(\"Error: {:?}\", err),\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* The behavior of `?` depends on whether this function returns a success result or an error result:\n",
|
||
" * on success, it unwraps the Result to get the success value inside.\n",
|
||
" * on error, it immediately returns from the enclosing function, passing the error result up the call chain. To ensure that this works, `?` can only be used in functions that have a `Result` return type."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Working with Multiple Error Types"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 14,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"ename": "Error",
|
||
"evalue": "`?` couldn't convert the error to `std::io::Error`",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m[E0277] Error:\u001b[0m `?` couldn't convert the error to `std::io::Error`",
|
||
" \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_14:1:1 \u001b[38;5;246m]\u001b[0m",
|
||
" \u001b[38;5;246m│\u001b[0m",
|
||
" \u001b[38;5;246m3 │\u001b[0m \u001b[38;5;249mf\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m:\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m&\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249my\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mB\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mf\u001b[0m\u001b[38;5;249mR\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249md\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m-\u001b[0m\u001b[38;5;249m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;100mR\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100ms\u001b[0m\u001b[38;5;100mu\u001b[0m\u001b[38;5;100ml\u001b[0m\u001b[38;5;100mt\u001b[0m\u001b[38;5;100m<\u001b[0m\u001b[38;5;100mV\u001b[0m\u001b[38;5;100me\u001b[0m\u001b[38;5;100mc\u001b[0m\u001b[38;5;100m<\u001b[0m\u001b[38;5;100mi\u001b[0m\u001b[38;5;100m3\u001b[0m\u001b[38;5;100m2\u001b[0m\u001b[38;5;100m>\u001b[0m\u001b[38;5;100m,\u001b[0m\u001b[38;5;100m \u001b[0m\u001b[38;5;100mi\u001b[0m\u001b[38;5;100mo\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100m:\u001b[0m\u001b[38;5;100mE\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100mo\u001b[0m\u001b[38;5;100mr\u001b[0m\u001b[38;5;100m>\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m{\u001b[0m",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m┬\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;100m╰\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m\u001b[38;5;100m─\u001b[0m expected `std::io::Error` because of this",
|
||
" \u001b[38;5;240m │\u001b[0m ",
|
||
" \u001b[38;5;246m7 │\u001b[0m \u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;54mp\u001b[0m\u001b[38;5;54ma\u001b[0m\u001b[38;5;54mr\u001b[0m\u001b[38;5;54ms\u001b[0m\u001b[38;5;54me\u001b[0m\u001b[38;5;54m:\u001b[0m\u001b[38;5;54m:\u001b[0m\u001b[38;5;54m<\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54m3\u001b[0m\u001b[38;5;54m2\u001b[0m\u001b[38;5;54m>\u001b[0m\u001b[38;5;54m(\u001b[0m\u001b[38;5;54m)\u001b[0m\u001b[38;5;68m?\u001b[0m\u001b[38;5;249m;\u001b[0m",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;68m┬\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m this can't be annotated with `?` because it has type `Result<_, ParseIntError>`",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m│\u001b[0m ",
|
||
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;68m╰\u001b[0m\u001b[38;5;68m─\u001b[0m\u001b[38;5;68m─\u001b[0m the trait `From<ParseIntError>` is not implemented for `std::io::Error`",
|
||
"\u001b[38;5;246m───╯\u001b[0m"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"use std::io::{self, BufRead};\n",
|
||
"\n",
|
||
"fn read_numbers(file: &mut dyn BufRead) -> Result<Vec<i32>, io::Error> {\n",
|
||
" let mut numbers = Vec::new();\n",
|
||
" for line_result in file.lines() {\n",
|
||
" let line = line_result?;\n",
|
||
" let number = line.parse::<i32>()?;\n",
|
||
" numbers.push(number);\n",
|
||
" }\n",
|
||
" Ok(numbers)\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"#### Case 1 - Trait `From`"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 17,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"use std::io::BufRead;\n",
|
||
"\n",
|
||
"#[derive(Debug)]\n",
|
||
"enum ReadNumbersError {\n",
|
||
" Io(io::Error),\n",
|
||
" ParseInt(std::num::ParseIntError),\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl From<io::Error> for ReadNumbersError {\n",
|
||
" fn from(err: io::Error) -> Self {\n",
|
||
" ReadNumbersError::Io(err)\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"impl From<std::num::ParseIntError> for ReadNumbersError {\n",
|
||
" fn from(err: std::num::ParseIntError) -> Self {\n",
|
||
" ReadNumbersError::ParseInt(err)\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn read_numbers(file: &mut dyn BufRead) -> Result<Vec<i32>, ReadNumbersError> {\n",
|
||
" let mut numbers = Vec::new();\n",
|
||
" for line_result in file.lines() {\n",
|
||
" let line = line_result?;\n",
|
||
" let number = line.parse::<i32>()?;\n",
|
||
" numbers.push(number);\n",
|
||
" }\n",
|
||
" Ok(numbers)\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 18,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"[1, 2, 3, 42, 665, 6, 7, 8]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"use std::io::{self, BufReader};\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" let file = std::fs::File::open(\"numbers.txt\").unwrap();\n",
|
||
"\n",
|
||
" let mut buffer_reader = BufReader::new(file);\n",
|
||
"\n",
|
||
" let numbers_result = read_numbers(&mut buffer_reader);\n",
|
||
"\n",
|
||
" match numbers_result {\n",
|
||
" Ok(numbers) => println!(\"{:?}\", numbers),\n",
|
||
" Err(e) => println!(\"Error reading numbers: {:?}\", e),\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"#### Case 2 - `Box<dyn std::error::Error>`"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* Code does not compile - there is no conversion from `ParseIntError` to `io::Error`\n",
|
||
"* The easiest way to handle multiple error types is to define these type aliases:"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 19,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"type GenericError = Box<dyn std::error::Error>;\n",
|
||
"type GenericResult<T> = Result<T, GenericError>;"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"* and change return type from function to `GenericResult<Vec<i32>>`"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 20,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"use std::io::{self, BufRead};\n",
|
||
"\n",
|
||
"fn parse_numbers(file: &mut dyn BufRead) -> GenericResult<Vec<i32>> {\n",
|
||
" let mut numbers = Vec::new();\n",
|
||
" for line_result in file.lines() {\n",
|
||
" let line = line_result?;\n",
|
||
" let number = line.parse::<i32>()?;\n",
|
||
" numbers.push(number);\n",
|
||
" }\n",
|
||
" Ok(numbers)\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 21,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Error reading numbers: ParseIntError { kind: InvalidDigit }\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"use std::io::{self, BufReader};\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" let file = std::fs::File::open(\"invalid_numbers.txt\").unwrap();\n",
|
||
" let mut buffer_reader = BufReader::new(file);\n",
|
||
" \n",
|
||
" let numbers_result = parse_numbers(&mut buffer_reader);\n",
|
||
"\n",
|
||
" match numbers_result {\n",
|
||
" Ok(numbers) => println!(\"{:?}\", numbers),\n",
|
||
" Err(e) => println!(\"Error reading numbers: {:?}\", e),\n",
|
||
" }\n",
|
||
"}\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Declaring a Custom Error Types"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 22,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"#[derive(Debug, Clone)]\n",
|
||
"pub struct JsonError {\n",
|
||
" pub message: String,\n",
|
||
" pub line: usize,\n",
|
||
" pub column: usize,\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 23,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"use std;\n",
|
||
"use std::fmt;\n",
|
||
"\n",
|
||
"// Errors should be printable.\n",
|
||
"impl fmt::Display for JsonError {\n",
|
||
" fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {\n",
|
||
" write!(f, \"{} ({}:{})\", self.message, self.line, self.column)\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"// Errors should implement the std::error::Error trait.\n",
|
||
"impl std::error::Error for JsonError {\n",
|
||
" fn description(&self) -> &str {\n",
|
||
" &self.message\n",
|
||
" }\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 24,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
":dep serde_json = \"1.0.64\"\n",
|
||
":dep serde = { version=\"1.0.127\", features=[\"derive\"] }\n",
|
||
"\n",
|
||
"use serde::{Deserialize, Serialize};\n",
|
||
"\n",
|
||
"#[derive(Serialize, Deserialize, Debug)]\n",
|
||
"struct Person {\n",
|
||
" name: String,\n",
|
||
" age: i32,\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn load_from_json() -> Result<Person, JsonError> {\n",
|
||
" let json = r#\"\n",
|
||
" {\n",
|
||
" \"name\": \"John\",\n",
|
||
" \"age\": 30\n",
|
||
" }\n",
|
||
" \"#;\n",
|
||
"\n",
|
||
" let person: Person = match serde_json::from_str(json) {\n",
|
||
" Ok(data) => data,\n",
|
||
" Err(err) => {\n",
|
||
" let line = err.line();\n",
|
||
" let column = err.column();\n",
|
||
" let message = err.to_string();\n",
|
||
" return Err(JsonError { message, line, column });\n",
|
||
" }\n",
|
||
" };\n",
|
||
"\n",
|
||
" Ok(person)\n",
|
||
"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 14,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"None"
|
||
]
|
||
},
|
||
"execution_count": 14,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"let person = load_from_json().err();\n",
|
||
"person"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Crates for Error Handling"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## thiserror\n",
|
||
"\n",
|
||
"* The `thiserror` crate provides a convenient way to define custom error types in Rust by using procedural macros. It simplifies the process of implementing the `std::error::Error` trait and provides useful features for error handling.\n",
|
||
"* With `thiserror`, you can define your error types as enums or structs and annotate them with the `#[derive(Error)]` attribute. This automatically generates the necessary implementations for the `Error` trait, including methods like `source()` and `description()`.\n",
|
||
"* The crate also allows you to specify error messages using the `#[error(\"...\")]` attribute, which supports formatting and interpolation of fields within the error type.\n",
|
||
"* Additionally, `thiserror` supports automatic conversion from other error types using the `#[from]` attribute, making it easy to wrap and propagate errors from different sources."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 25,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Error parsing numbers: Failed to parse number: 66d5\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
":dep thiserror = \"2.0\"\n",
|
||
"\n",
|
||
"mod thiserror_example {\n",
|
||
" use thiserror::Error;\n",
|
||
"\n",
|
||
" #[derive(Error, Debug)]\n",
|
||
" pub enum ParseNumberError {\n",
|
||
" #[error(\"File not found\")]\n",
|
||
" NotFound(#[from] std::io::Error), // automatic conversion from std::io::Error\n",
|
||
" #[error(\"Failed to parse number: {0}\")]\n",
|
||
" ParseError(String),\n",
|
||
" }\n",
|
||
"\n",
|
||
" pub fn parse_numbers(path: &str) -> Result<Vec<i32>, ParseNumberError> {\n",
|
||
" use std::fs::File;\n",
|
||
" use std::io::{self, BufRead, BufReader};\n",
|
||
"\n",
|
||
" let file = File::open(path)?;\n",
|
||
" let reader = BufReader::new(file);\n",
|
||
" let mut numbers = Vec::new();\n",
|
||
"\n",
|
||
" for line_result in reader.lines() {\n",
|
||
" let line = line_result?;\n",
|
||
" match line.parse::<i32>() {\n",
|
||
" Ok(num) => numbers.push(num),\n",
|
||
" Err(_) => return Err(ParseNumberError::ParseError(line)),\n",
|
||
" }\n",
|
||
" }\n",
|
||
"\n",
|
||
" Ok(numbers)\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" match thiserror_example::parse_numbers(\"invalid_numbers.txt\") {\n",
|
||
" Ok(numbers) => println!(\"Parsed numbers: {:?}\", numbers),\n",
|
||
" Err(e) => println!(\"Error parsing numbers: {}\", e),\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## anyhow\n",
|
||
"\n",
|
||
"* The `anyhow` crate provides a simple and flexible way to handle errors in Rust applications. It allows you to create and propagate errors without having to define custom error types for every possible error scenario.\n",
|
||
"* With `anyhow`, you can use the `anyhow::Error` type to represent errors, which can encapsulate any error type that implements the `std::error::Error` trait. This makes it easy to work with different error types without having to define a separate error enum or struct for each case.\n",
|
||
"* The crate also provides convenient macros like `anyhow!` for creating errors with formatted messages, and the `Context` trait for adding additional context to errors as they propagate through your code.\n",
|
||
"* Library convinent when prototyping or building applications where you want to focus on error handling without the overhead of defining numerous custom error types."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 26,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"[1, 2, 3, 42, 665, 6, 7, 8]\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
":dep anyhow = \"1.0\"\n",
|
||
"\n",
|
||
"mod anyhow_example {\n",
|
||
" use anyhow::Context;\n",
|
||
" use std::io::{BufRead, BufReader};\n",
|
||
"\n",
|
||
" pub fn parse_numbers(file: &mut dyn BufRead) -> anyhow::Result<Vec<i32>> {\n",
|
||
" let mut numbers = Vec::new();\n",
|
||
" for (index, line_result) in file.lines().enumerate() {\n",
|
||
" let line = line_result.context(format!(\"Failed to read line {}\", index + 1))?;\n",
|
||
" let number = line.parse::<i32>()\n",
|
||
" .context(format!(\"Failed to parse '{}' as i32 on line {}\", line, index + 1))?;\n",
|
||
" numbers.push(number);\n",
|
||
" }\n",
|
||
" Ok(numbers)\n",
|
||
" }\n",
|
||
"\n",
|
||
" pub fn read_numbers(path: &str) -> anyhow::Result<Vec<i32>> {\n",
|
||
" let file = std::fs::File::open(path)\n",
|
||
" .with_context(|| format!(\"Failed to open file '{}'\", path))?;\n",
|
||
" \n",
|
||
" let mut buffer_reader = BufReader::new(file);\n",
|
||
" let numbers = parse_numbers(&mut buffer_reader)\n",
|
||
" .with_context(|| format!(\"Failed to read numbers from file '{}'\", path))?;\n",
|
||
" \n",
|
||
" Ok(numbers)\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn main() {\n",
|
||
" let result = anyhow_example::read_numbers(\"numbers.txt\"); \n",
|
||
" match result {\n",
|
||
" Ok(numbers) => println!(\"{:?}\", numbers),\n",
|
||
" Err(err) => println!(\"Error: {:?}\", err),\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 27,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"use anyhow::{anyhow, Result};\n",
|
||
"\n",
|
||
"fn get_user_age(name: &str) -> Result<u32> {\n",
|
||
" match name {\n",
|
||
" \"Alice\" => Ok(30),\n",
|
||
" \"Bob\" => Ok(25),\n",
|
||
" _ => Err(anyhow!(\"No age data available for user '{}'\", name)),\n",
|
||
" }\n",
|
||
"}\n",
|
||
"\n",
|
||
"fn main() -> Result<()> {\n",
|
||
" let age = get_user_age(\"Charlie\")?;\n",
|
||
" println!(\"User age: {}\", age);\n",
|
||
" Ok(())\n",
|
||
"}\n",
|
||
"\n",
|
||
"main();"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "Rust",
|
||
"language": "rust",
|
||
"name": "rust"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": "rust",
|
||
"file_extension": ".rs",
|
||
"mimetype": "text/rust",
|
||
"name": "rust",
|
||
"pygment_lexer": "rust",
|
||
"version": ""
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 2
|
||
}
|