Std error rust

Introduction to the Rust language, standard library and ecosystem

Basic Error Handling

Error handling in Rust can be clumsy if you can’t use the question-mark operator.
To achieve happiness, we need to return a Result which can accept any error.
All errors implement the trait std::error::Error, and
so any error can convert into a Box<Error>.

Say we needed to handle both i/o errors and errors from converting
strings into numbers:

# #![allow(unused_variables)]
# 
#fn main() {
// box-error.rs
use std::fs::File;
use std::io::prelude::*;
use std::error::Error;

fn run(file: &str) -> Result<i32,Box<Error>> {
    let mut file = File::open(file)?;
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    Ok(contents.trim().parse()?)
}

#}

So that’s two question-marks for the i/o errors (can’t open file, or can’t read as string)
and one question-mark for the conversion error. Finally, we wrap the result in Ok.
Rust can work out from the return type that parse should convert to i32.

It’s easy to create a shortcut for this Result type:

# #![allow(unused_variables)]
# 
#fn main() {
type BoxResult<T> = Result<T,Box<Error>>;

#}

However, our programs will have application-specific error conditions, and so
we need to create our own error type. The basic requirements
are straightforward:

  • May implement Debug
  • Must implement Display
  • Must implement Error

Otherwise, your error can do pretty much what it likes.

# #![allow(unused_variables)]
# 
#fn main() {
// error1.rs
use std::error::Error;
use std::fmt;

#[derive(Debug)]
struct MyError {
    details: String
}

impl MyError {
    fn new(msg: &str) -> MyError {
        MyError{details: msg.to_string()}
    }
}

impl fmt::Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,"{}",self.details)
    }
}

impl Error for MyError {
    fn description(&self) -> &str {
        &self.details
    }
}

// a test function that returns our error result
fn raises_my_error(yes: bool) -> Result<(),MyError> {
    if yes {
        Err(MyError::new("borked"))
    } else {
        Ok(())
    }
}

#}

Typing Result<T,MyError> gets tedious and many Rust modules define their own
Result — e.g. io::Result<T> is short for Result<T,io::Error>.

In this next example we need to handle the specific error when a string can’t be parsed
as a floating-point number.

Now the way that ? works
is to look for a conversion from the error of the expression to the error that must
be returned. And this conversion is expressed by the From trait. Box<Error>
works as it does because it implements From for all types implementing Error.

At this point you can continue to use the convenient alias BoxResult and catch everything
as before; there will be a conversion from our error into Box<Error>.
This is a good option for smaller applications. But I want to show other errors can
be explicitly made to cooperate with our error type.

ParseFloatError implements Error so description() is defined.

# #![allow(unused_variables)]
# 
#fn main() {
use std::num::ParseFloatError;

impl From<ParseFloatError> for MyError {
    fn from(err: ParseFloatError) -> Self {
        MyError::new(err.description())
    }
}

// and test!
fn parse_f64(s: &str, yes: bool) -> Result<f64,MyError> {
    raises_my_error(yes)?;
    let x: f64 = s.parse()?;
    Ok(x)
}

#}

The first ? is fine (a type always converts to itself with From) and the
second ? will convert the ParseFloatError to MyError.

And the results:

fn main() {
    println!(" {:?}", parse_f64("42",false));
    println!(" {:?}", parse_f64("42",true));
    println!(" {:?}", parse_f64("?42",false));
}
//  Ok(42)
//  Err(MyError { details: "borked" })
//  Err(MyError { details: "invalid float literal" })

Not too complicated, although a little long-winded. The tedious bit is having to
write From conversions for all the other error types that need to play nice
with MyError — or you simply lean on Box<Error>. Newcomers get confused
by the multitude of ways to do the same thing in Rust; there is always another
way to peel the avocado (or skin the cat, if you’re feeling bloodthirsty). The price
of flexibility is having many options. Error-handling for a 200 line program can afford
to be simpler than for a large application. And if you ever want to package your precious
droppings as a Cargo crate, then error handling becomes crucial.

Currently, the question-mark operator only works for Result, not Option, and this is
a feature, not a limitation. Option has a ok_or_else which converts itself into a Result.
For example, say we had a HashMap and must fail if a key isn’t defined:

# #![allow(unused_variables)]
# 
#fn main() {
    let val = map.get("my_key").ok_or_else(|| MyError::new("my_key not defined"))?;

#}

Now here the error returned is completely clear! (This form uses a closure, so the error value
is only created if the lookup fails.)

simple-error for Simple Errors

The simple-error crate provides you with
a basic error type based on a string, as we have defined it here, and a few convenient macros.
Like any error, it works fine with Box<Error>:

#[macro_use]
extern crate simple_error;

use std::error::Error;

type BoxResult<T> = Result<T,Box<Error>>;

fn run(s: &str) -> BoxResult<i32> {
    if s.len() == 0 {
        bail!("empty string");
    }
    Ok(s.trim().parse()?)
}

fn main() {
    println!("{:?}", run("23"));
    println!("{:?}", run("2x"));
    println!("{:?}", run(""));
}
// Ok(23)
// Err(ParseIntError { kind: InvalidDigit })
// Err(StringError("empty string"))

bail!(s) expands to return SimpleError::new(s).into(); — return early with a conversion into
the receiving type.

You need to use BoxResult for mixing the SimpleError type with other errors, since
we can’t implement From for it, since both the trait and the type come from other crates.

error-chain for Serious Errors

For non-trivial applications have a look
at the error_chain crate.
A little macro magic can go a long way in Rust…

Create a binary crate with cargo new --bin test-error-chain and
change to this directory. Edit Cargo.toml and add error-chain="0.8.1" to the end.

What error-chain does for you is create all the definitions we needed for manually implementing
an error type; creating a struct, and implementing the necessary traits: Display, Debug and Error.
It also by default implements From so strings can be converted into errors.

Our first src/main.rs file looks like this. All the main program does is call run, print out any
errors, and end the program with a non-zero exit code. The macro error_chain generates all the
definitions needed, within an error module — in a larger program you would put this in its own file.
We need to bring everything in error back into global scope because our code will need to see
the generated traits. By default, there will be an Error struct and a Result defined with that
error.

Here we also ask for From to be implemented so that std::io::Error will convert into
our error type using foreign_links:

#[macro_use]
extern crate error_chain;

mod errors {
    error_chain!{
        foreign_links {
            Io(::std::io::Error);
        }
    }
}
use errors::*;

fn run() -> Result<()> {
    use std::fs::File;

    File::open("file")?;

    Ok(())
}


fn main() {
    if let Err(e) = run() {
        println!("error: {}", e);

        std::process::exit(1);
    }
}
// error: No such file or directory (os error 2)

The ‘foreign_links’ has made our life easier, since the question mark operator now knows how to
convert std::io::Error into our error::Error. (Under the hood, the macro is creating a
From<std::io::Error> conversion, exactly as spelt out earlier.)

All the action happens in run; let’s make it print out the first 10 lines of a file given as the
first program argument. There may or may not be such an argument, which isn’t necessarily an
error. Here we want to convert an Option<String> into a Result<String>. There are two Option
methods for doing this conversion, and I’ve picked the simplest one. Our Error type implements
From for &str, so it’s straightforward to make an error with a simple text message.

# #![allow(unused_variables)]
# 
#fn main() {
fn run() -> Result<()> {
    use std::env::args;
    use std::fs::File;
    use std::io::BufReader;
    use std::io::prelude::*;

    let file = args().skip(1).next()
        .ok_or(Error::from("provide a file"))?;

    let f = File::open(&file)?;
    let mut l = 0;
    for line in BufReader::new(f).lines() {
        let line = line?;
        println!("{}", line);
        l += 1;
        if l == 10 {
            break;
        }
    }

    Ok(())
}

#}

There is (again) a useful little macro bail! for ‘throwing’ errors.
An alternative to the ok_or method here could be:

# #![allow(unused_variables)]
# 
#fn main() {
    let file = match args().skip(1).next() {
        Some(s) => s,
        None => bail!("provide a file")
    };

#}

Like ? it does an early return.

The returned error contains an enum ErrorKind, which allows us to distinguish between various
kinds of errors. There’s always a variant Msg (when you say Error::from(str)) and the foreign_links
has declared Io which wraps I/O errors:

fn main() {
    if let Err(e) = run() {
        match e.kind() {
            &ErrorKind::Msg(ref s) => println!("msg {}",s),
            &ErrorKind::Io(ref s) => println!("io {}",s),
        }
        std::process::exit(1);
    }
}
// $ cargo run
// msg provide a file
// $ cargo run foo
// io No such file or directory (os error 2)

It’s straightforward to add new kinds of errors. Add an errors section to the error_chain! macro:

# #![allow(unused_variables)]
# 
#fn main() {
    error_chain!{
        foreign_links {
            Io(::std::io::Error);
        }

        errors {
            NoArgument(t: String) {
                display("no argument provided: '{}'", t)
            }
        }

    }

#}

This defines how Display works for this new kind of error. And now we can handle
‘no argument’ errors more specifically, feeding ErrorKind::NoArgument a String value:

# #![allow(unused_variables)]
# 
#fn main() {
    let file = args().skip(1).next()
        .ok_or(ErrorKind::NoArgument("filename needed".to_string()))?;


#}

There’s now an extra ErrorKind variant that you must match:

fn main() {
    if let Err(e) = run() {
        println!("error {}",e);
        match e.kind() {
            &ErrorKind::Msg(ref s) => println!("msg {}", s),
            &ErrorKind::Io(ref s) => println!("io {}", s),
            &ErrorKind::NoArgument(ref s) => println!("no argument {:?}", s),
        }
        std::process::exit(1);
    }
}
// cargo run
// error no argument provided: 'filename needed'
// no argument "filename needed"

Generally, it’s useful to make errors as specific as possible, particularly if this is a library
function! This match-on-kind technique is pretty much the equivalent of traditional exception handling,
where you match on exception types in a catch or except block.

In summary, error-chain creates a type Error for you, and defines Result<T> to be std::result::Result<T,Error>.
Error contains an enum ErrorKind and by default there is one variant Msg for errors created from
strings. You define external errors with foreign_links which does two things. First, it creates a new
ErrorKind variant. Second, it defines From on these external errors so they can be converted to our
error. New error variants can be easily added. A lot of irritating boilerplate code is eliminated.

Chaining Errors

But the really cool thing that this crate provides is error chaining.

As a library user, it’s irritating when a method simply just ‘throws’ a generic I/O error. OK, it
could not open a file, fine, but what file? Basically, what use is this information to me?

error_chain does error chaining which helps solve this problem of over-generic errors. When we
try to open the file, we can lazily lean on the conversion to io::Error using ?, or chain the error.

# #![allow(unused_variables)]
# 
#fn main() {
// non-specific error
let f = File::open(&file)?;

// a specific chained error
let f = File::open(&file).chain_err(|| "unable to read the damn file")?;

#}

Here’s a new version of the program, with no imported ‘foreign’ errors, just the defaults:

#[macro_use]
extern crate error_chain;

mod errors {
    error_chain!{
    }

}
use errors::*;

fn run() -> Result<()> {
    use std::env::args;
    use std::fs::File;
    use std::io::BufReader;
    use std::io::prelude::*;

    let file = args().skip(1).next()
        .ok_or(Error::from("filename needed"))?;

    ///////// chain explicitly! ///////////
    let f = File::open(&file).chain_err(|| "unable to read the damn file")?;

    let mut l = 0;
    for line in BufReader::new(f).lines() {
        let line = line.chain_err(|| "cannot read a line")?;
        println!("{}", line);
        l += 1;
        if l == 10 {
            break;
        }
    }

    Ok(())
}


fn main() {
    if let Err(e) = run() {
        println!("error {}", e);

        /////// look at the chain of errors... ///////
        for e in e.iter().skip(1) {
            println!("caused by: {}", e);
        }

        std::process::exit(1);
    }
}
// $ cargo run foo
// error unable to read the damn file
// caused by: No such file or directory (os error 2)

So the chain_err method takes the original error, and creates a new error which contains the
original error — this can be continued indefinitely. The closure is expected to return any
value which can be converted into an error.

Rust macros can clearly save you a lot of typing. error-chain even provides a shortcut that
replaces the whole main program:

# #![allow(unused_variables)]
# 
#fn main() {
quick_main!(run);

#}

(run is where all the action takes place, anyway.)

I started doing university lectures on Rust, as well as holding workshops and trainings. One of the parts that evolved from a couple of slides into a full-blown session was everything around error handling in Rust, since it’s so incredibly good!

Not only does it help making impossible states impossible, but there’s also so much detail to it that handling errors – much like everything in Rust – becomes very ergonomic and easy to read and use.

Making impossible states impossible #

In Rust, there are no things like undefined or null, nor do you have exceptions like you know it from programming languages like Java or C#. Instead, you use built-in enums to model state:

  • Option<T> for bindings that might possibly have no value (e.g. Some(x) or None)
  • Result<T, E> for results from operations that might error (e.g. Ok(val) vs Err(error))

The difference between the two is very nuanced and depends a lot on the semantics of your code. The way both enums work is very similar though. The most important thing, in my opinion, is that both types request from you to deal with them. Either by explicitly handling all states, or by explicitly ignoring them.

In this article, I want to focus on Result<T, E> as this one actually contains errors.

Result<T, E> is an enum with two variants:

enum Result<T, E> {
Ok(T),
Err(E),
}

T, E are generics. T can be any value, E can be any error. The two variants Ok and Err are globally available.

Use Result<T, E> when you have things that might go wrong. An operation that is expected to succeed, but there might be cases where it doesn’t. Once you have a Result value, you can do the following:

  • Deal with the states!
  • Ignore it
  • Panic!
  • Use fallbacks
  • Propagate errors

Let’s see what I mean in detail.

Deal with the error state #

Let’s write a little piece where we want to read a string from a file. It requires us to

  1. Read a file
  2. Read a string from this file

Both operations might cause a std::io::Error because something unforeseen can happen (the file doesn’t exist, or it can’t be read from, etc.). So the function we’re writing can return either a String or an io::Error.

use std::io;
use std::fs::File;

fn read_username_from_file(path: &str) -> Result<String, io::Error> {
let f = File::open(path);

/* 1 */
let mut f = match f {
Ok(file) => file,
Err(e) => return Err(e),
};

let mut s = String::new();

/* 2 */
match f.read_to_string(&mut s) {
Ok(_) => Ok(s),
Err(err) => Err(err),
}
}

This is what happens:

  1. When we open a file from path, it either can return a filehandle to work with Ok(file), or it causes an error Err(e). With match f we’re forced to deal with the two possible states. Either we assign the filehandle to f (notice the shadowing of f), or we return from the function by returning the error. The return statement here is important as we want to exit the function.
  2. We then want to read the contents into s, the string we just created. It again can either succeed or throw an error. The function f.read_to_string returns the length of bytes read, so we can safely ignore the value and return an Ok(s) with the string read. In the other case, we just return the same error. Note that I didn’t write a semi-colon at the end of the match expression. Since it’s an expression, this is what we return from the function at this point.

This might look very verbose (it is…), but you see two very important aspects of error handling:

  1. In both cases you’re expected to deal with the two possible states. You can’t continue if don’t do something
  2. Features like shadowing (binding a value to an existing name) and expressions make even verbose code easy to read and use

The operation we just did is often called unwrapping. Because you unwrap the value that is wrapped inside the enum.

Speaking of unwrapping

Ignore the errors #

If you’re very confident that your program won’t fail, you can simply .unwrap() your values using the built-in functions:

fn read_username_from_file(path: &str) -> Result<String, io::Error> {
let mut f = File::open(path).unwrap(); /* 1 */
let mut s = String::new();
f.read_to_string(&mut s).unwrap(); /* 1 */
Ok(s) /* 2 */
}

Here’s what happens:

  1. In all cases that might cause an error, we’re calling unwrap() to get to the value
  2. We wrap the result in an Ok variant which we return. We could just return s and drop the Result<T, E> in our function signature. We keep it because we use it in the other examples again.

The unwrap() function itself is very much like what we did in the first step where we dealt with all states:

// result.rs

impl<T, E: fmt::Debug> Result<T, E> {
// ...

pub fn unwrap(&self) -> T {
match self {
Ok(t) => t,
Err(e) => unwrap_failed("called `Result::unwrap()` on an `Err` value", &e),
}
}

// ...
}

unwrap_failed is a shortcut to the panic! macro. This means if you use .unwrap() and you don’t have a successful result, your software crashes. 😱

You might ask yourself: How is this different from errors that just crash the software in other programming languages? The answer is easy: You have to be explicit about it. Rust requires you to do something, even if it’s explicitly allowing to panic.

There are lots of different .unwrap_ functions you can use for various situations. We look at one or two of them further on.

Panic! #

Speaking of panics, you can also panic with your own panic message:

fn read_username_from_file(path: &str) -> Result<String, io::Error> {
let mut f = File::open(path).expect("Error opening file");
let mut s = String::new();
f.read_to_string(&mut s).unwrap("Error reading file to string");
Ok(s)
}

What .expect(...) does is very similar to unwrap()

impl<T, E: fmt::Debug> Result<T, E> {
// ...
pub fn expect(self, msg: &str) -> T {
match self {
Ok(t) => t,
Err(e) => unwrap_failed(msg, &e),
}
}
}

But, you have your panic messages in your hand, which you might like!

But even if we are explicit at all times, we may want our software not to panic and crash whenever we encounter an error state. We might want to do something useful, like providing fallbacks or … well … actually handling errors.

Fallback values #

Rust has the possibility to use default values on their Result (and Option) enums.

fn read_username_from_file(path: &str) -> Result<String, io::Error> {
let mut f = File::open(path).expect("Error opening file");
let mut s = String::new();
f.read_to_string(&mut s).unwrap_or("admin"); /* 1 */
Ok(s)
}
  1. "admin" might not be the best fallback for a username, but you get the idea. Instead of crashing, we return a default value in the case of an error result. The method .unwrap_or_else takes a closure for more complex default values.

That’s better! Still, what we’ve learned so far is a trade-off between being very verbose, or allowing for explicit crashes, or maybe having fallback values. But can we have both? Concise code and error safety? We can!

Propagate the error #

One of the features I love most with Rust’s Result types is the possibility to propagate an error. Both functions that might cause an error have the same error type: io::Error. We can use the question mark operator after each operation to write code for the happy path (only success results), and return error results if something goes wrong:

fn read_username_from_file(path: &str) -> Result<String, io::Error> {
let mut f = File::open(path)?;
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}

In this piece, f is a file handler, f.read_to_string saves to s. If anything goes wrong, we return from the function with Err(io::Error). Concise code, but we deal with the error one level above:

fn main() {
match read_username_from_file("user.txt") {
Ok(username) => println!("Welcome {}", username),
Err(err) => eprintln!("Whoopsie! {}", err)
};
}

The great thing about it?

  1. We are still explicit, we have to do something! You can still find all the spots where errors can happen!
  2. We can write concise code as if errors wouldn’t exist. Errors still have to be dealt with! Either from us or from the users of our function.

The question mark operator also works on Option<T>, this also allows for some really nice and elegant code!

Propagating different errors #

The problem is though, that methods like this only work when the error types are the same. If we have two different types of errors, we have to get creative. Look at this slightly modified function, where we open and read files, but then parse the read content into a u64

fn read_number_from_file(filename: &str) -> Result<u64, ???> {
let mut file = File::open(filename)?; /* 1 */

let mut buffer = String::new();
file.read_to_string(&mut buffer)?; /* 1 */

let parsed: u64 = buffer.trim().parse()?; /* 2 */

Ok(parsed)
}

  1. These two spots can cause io::Error, as we know from the previous examples
  2. This operation however can cause a ParseIntError

The problem is, we don’t know which error we get at compile time. This is entirely up to our code running. We could handle each error through match expressions and return our own error type. Which is valid, but makes our code verbose again. Or we prepare for “things that happen at runtime”!

Check out our slightly changed function

use std::error;

fn read_number_from_file(filename: &str) -> Result<u64, Box<dyn error::Error>> {
let mut file = File::open(filename)?; /* 1 */

let mut buffer = String::new();
file.read_to_string(&mut buffer)?; /* 1 */

let parsed: u64 = buffer.trim().parse()?; /* 2 */

Ok(parsed)
}

This is what happens:

  • Instead of returning an error implementation, we tell Rust that something that implements the Error error trait is coming along.
  • Since we don’t know what this can be at compile-time, we have to make it a trait object: dyn std::error::Error.
  • And since we don’t know how big this will be, we wrap it in a Box. A smart pointer that points to data that will be eventually on the heap

A Box<dyn Trait> enables dynamic dispatch in Rust: The possibility to dynamically call a function that is not known at compile time. For that, Rust introduces a vtable that keeps pointers to the actual implementations. At runtime, we use these pointers to invoke the appropriate function implementations.

Memory layout of Box and Box

And now, our code is concise again, and our users have to deal with the eventual error.

The first question I get when I show this to folks in my courses is: But can we eventually check which type of error has happened? We can! The downcast_ref() method allows us to get back to the original type.

fn main() {
match read_number_from_file("number.txt") {
Ok(v) => println!("Your number is {}", v),
Err(err) => {
if let Some(io_err) = err.downcast_ref::<std::io::Error>() {
eprintln!("Error during IO! {}", io_err)
} else if let Some(pars_err) = err.downcast_ref::<ParseIntError>() {
eprintln!("Error during parsing {}", pars_err)
}
}
};
}

Groovy!

Custom errors #

It’s getting even better and more flexible if you want to create custom errors for your operations. To use custom errors, your error structs have to implement the std::error::Error trait. This can be a classic struct, a tuple struct or even a unit struct.

You don’t have to implement any functions of std::error::Error, but you need to implement both the Debug and the Display trait. The reasoning is that errors want to be printed somewhere. Here’s how an example looks like:

#[derive(Debug)] /* 1 */
pub struct ParseArgumentsError(String); /* 2 */

impl std::error::Error for ParseArgumentsError {} /* 3 */

/* 4 */
impl Display for ParseArgumentsError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}

  1. We derive the Debug trait.
  2. Our ParseArgumentsError is a tuple struct with one element: A custom message
  3. We implement std::error::Error for ParseArgumentsError. No need to implement anything else
  4. We implement Display, where we print out the single element of our tuple.

And that’s it!

Anyhow… #

Since a lot of the things you just learned a very common, there are of course crates available that abstract most of it. The fantastic anyhow crate is one of them and gives you trait object-based error handling with convenience macros and types.

Bottom line #

This is a very quick primer on error handling in Rust. There is of course more to it, but it should get you started! This is also my first technical Rust article, and I hope many more are coming. Let me know if you liked it and if you find any … haha … errors (ba-dum-ts 🥁), I’m just a tweet away.

Struct std::io::Error

pub struct Error { /* fields omitted */ }

The error type for I/O operations of the Read, Write, Seek, and associated traits.

Errors mostly originate from the underlying OS, but custom instances of Error can be created with crafted error messages and a particular value of ErrorKind.

impl Error[src]

pub fn new<E>(kind: ErrorKind, error: E) -> Error where
    E: Into<Box<dyn Error + Send + Sync>>, 
[src]

Creates a new I/O error from a known kind of error as well as an arbitrary error payload.

This function is used to generically create I/O errors which do not originate from the OS itself. The error argument is an arbitrary payload which will be contained in this Error.

use std::io::{Error, ErrorKind};

// errors can be created from strings
let custom_error = Error::new(ErrorKind::Other, "oh no!");

// errors can also be created from other errors
let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);

pub fn last_os_error() -> Error[src]

Returns an error representing the last OS error which occurred.

This function reads the value of errno for the target platform (e.g. GetLastError on Windows) and will return a corresponding instance of Error for the error code.

use std::io::Error;

println!("last OS error: {:?}", Error::last_os_error());

pub fn from_raw_os_error(code: i32) -> Error[src]

Creates a new instance of an Error from a particular OS error code.

On Linux:

use std::io;

let error = io::Error::from_raw_os_error(22);
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);

On Windows:

use std::io;

let error = io::Error::from_raw_os_error(10022);
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);

pub fn raw_os_error(&self) -> Option<i32>[src]

Returns the OS error that this error represents (if any).

If this Error was constructed via last_os_error or from_raw_os_error, then this function will return Some, otherwise it will return None.

use std::io::{Error, ErrorKind};

fn print_os_error(err: &Error) {
    if let Some(raw_os_err) = err.raw_os_error() {
        println!("raw OS error: {:?}", raw_os_err);
    } else {
        println!("Not an OS error");
    }
}

fn main() {
    // Will print "raw OS error: ...".
    print_os_error(&Error::last_os_error());
    // Will print "Not an OS error".
    print_os_error(&Error::new(ErrorKind::Other, "oh no!"));
}

pub fn get_ref(&self) -> Option<&(dyn Error + Send + Sync + 'static)>[src]1.3.0

Returns a reference to the inner error wrapped by this error (if any).

If this Error was constructed via new then this function will return Some, otherwise it will return None.

use std::io::{Error, ErrorKind};

fn print_error(err: &Error) {
    if let Some(inner_err) = err.get_ref() {
        println!("Inner error: {:?}", inner_err);
    } else {
        println!("No inner error");
    }
}

fn main() {
    // Will print "No inner error".
    print_error(&Error::last_os_error());
    // Will print "Inner error: ...".
    print_error(&Error::new(ErrorKind::Other, "oh no!"));
}

pub fn get_mut(&mut self) -> Option<&mut (dyn Error + Send + Sync + 'static)>[src]1.3.0

Returns a mutable reference to the inner error wrapped by this error (if any).

If this Error was constructed via new then this function will return Some, otherwise it will return None.

use std::io::{Error, ErrorKind};
use std::{error, fmt};
use std::fmt::Display;

#[derive(Debug)]
struct MyError {
    v: String,
}

impl MyError {
    fn new() -> MyError {
        MyError {
            v: "oh no!".to_string()
        }
    }

    fn change_message(&mut self, new_message: &str) {
        self.v = new_message.to_string();
    }
}

impl error::Error for MyError {}

impl Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "MyError: {}", &self.v)
    }
}

fn change_error(mut err: Error) -> Error {
    if let Some(inner_err) = err.get_mut() {
        inner_err.downcast_mut::<MyError>().unwrap().change_message("I've been changed!");
    }
    err
}

fn print_error(err: &Error) {
    if let Some(inner_err) = err.get_ref() {
        println!("Inner error: {}", inner_err);
    } else {
        println!("No inner error");
    }
}

fn main() {
    // Will print "No inner error".
    print_error(&change_error(Error::last_os_error()));
    // Will print "Inner error: ...".
    print_error(&change_error(Error::new(ErrorKind::Other, MyError::new())));
}

pub fn into_inner(self) -> Option<Box<dyn Error + Send + Sync>>[src]1.3.0

Consumes the Error, returning its inner error (if any).

If this Error was constructed via new then this function will return Some, otherwise it will return None.

use std::io::{Error, ErrorKind};

fn print_error(err: Error) {
    if let Some(inner_err) = err.into_inner() {
        println!("Inner error: {}", inner_err);
    } else {
        println!("No inner error");
    }
}

fn main() {
    // Will print "No inner error".
    print_error(Error::last_os_error());
    // Will print "Inner error: ...".
    print_error(Error::new(ErrorKind::Other, "oh no!"));
}

pub fn kind(&self) -> ErrorKind[src]

Returns the corresponding ErrorKind for this error.

use std::io::{Error, ErrorKind};

fn print_error(err: Error) {
    println!("{:?}", err.kind());
}

fn main() {
    // Will print "Other".
    print_error(Error::last_os_error());
    // Will print "AddrInUse".
    print_error(Error::new(ErrorKind::AddrInUse, "oh no!"));
}

impl Debug for Error[src]

impl Display for Error[src]

impl Error for Error[src]

fn description(&self) -> &str[src]

👎 Deprecated since 1.42.0: use the Display impl or to_string()

if let Err(e) = "xc".parse::<u32>() { // Print e itself, no need for description(). eprintln!("Error: {}", e); } Read more

fn cause(&self) -> Option<&dyn Error>[src]

👎 Deprecated since 1.33.0: replaced by Error::source, which can support downcasting

fn source(&self) -> Option<&(dyn Error + 'static)>[src]

The lower-level source of this error, if any. Read more

fn backtrace(&self) -> Option<&Backtrace>[src]

🔬 This is a nightly-only experimental API. (backtrace #53487)

Returns a stack backtrace, if available, of where this error occurred. Read more

impl From<ErrorKind> for Error[src]1.14.0

Intended for use for errors not exposed to the user, where allocating onto the heap (for normal construction via Error::new) is too costly.

fn from(kind: ErrorKind) -> Error[src]

Converts an ErrorKind into an Error.

This conversion allocates a new error with a simple representation of error kind.

use std::io::{Error, ErrorKind};

let not_found = ErrorKind::NotFound;
let error = Error::from(not_found);
assert_eq!("entity not found", format!("{}", error));

impl<W> From<IntoInnerError<W>> for Error[src]

impl From<NulError> for Error[src]

impl !RefUnwindSafe for Error

impl Send for Error

impl Sync for Error

impl Unpin for Error

impl !UnwindSafe for Error

impl<T> Any for T where
    T: 'static + ?Sized, 
[src]

impl<T> Borrow<T> for T where
    T: ?Sized, 
[src]

fn borrow(&self) -> &T

Notable traits for &'_ mut F

impl<'_, F> Future for &'_ mut F where
    F: Unpin + Future + ?Sized, 
    type Output = <F as Future>::Output;
impl<'_, I> Iterator for &'_ mut I where
    I: Iterator + ?Sized, 
    type Item = <I as Iterator>::Item;
impl<R: Read + ?Sized, '_> Read for &'_ mut R
impl<W: Write + ?Sized, '_> Write for &'_ mut W

[src]

Immutably borrows from an owned value. Read more

impl<T> BorrowMut<T> for T where
    T: ?Sized, 
[src]

fn borrow_mut(&mut self) -> &mut T

Notable traits for &'_ mut F

impl<'_, F> Future for &'_ mut F where
    F: Unpin + Future + ?Sized, 
    type Output = <F as Future>::Output;
impl<'_, I> Iterator for &'_ mut I where
    I: Iterator + ?Sized, 
    type Item = <I as Iterator>::Item;
impl<R: Read + ?Sized, '_> Read for &'_ mut R
impl<W: Write + ?Sized, '_> Write for &'_ mut W

[src]

Mutably borrows from an owned value. Read more

impl<T> From<T> for T[src]

fn from(t: T) -> T[src]

Performs the conversion.

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

fn into(self) -> U[src]

Performs the conversion.

impl<T> ToString for T where
    T: Display + ?Sized, 
[src]

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

1.0.0[][src]Struct std::io::Error

The error type for I/O operations of the Read, Write, Seek, and
associated traits.

Errors mostly originate from the underlying OS, but custom instances of
Error can be created with crafted error messages and a particular value of
ErrorKind.

impl Error[src]

pub fn new<E>(kind: ErrorKind, error: E) -> Error where
    E: Into<Box<dyn Error + 'static + Sync + Send>>, 
[src]

Creates a new I/O error from a known kind of error as well as an
arbitrary error payload.

This function is used to generically create I/O errors which do not
originate from the OS itself. The error argument is an arbitrary
payload which will be contained in this Error.

use std::io::{Error, ErrorKind};


let custom_error = Error::new(ErrorKind::Other, "oh no!");


let custom_error2 = Error::new(ErrorKind::Interrupted, custom_error);

pub fn last_os_error() -> Error[src]

Returns an error representing the last OS error which occurred.

This function reads the value of errno for the target platform (e.g.
GetLastError on Windows) and will return a corresponding instance of
Error for the error code.

use std::io::Error;

println!("last OS error: {:?}", Error::last_os_error());

pub fn from_raw_os_error(code: i32) -> Error[src]

Creates a new instance of an Error from a particular OS error code.

On Linux:

use std::io;

let error = io::Error::from_raw_os_error(22);
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);

On Windows:

use std::io;

let error = io::Error::from_raw_os_error(10022);
assert_eq!(error.kind(), io::ErrorKind::InvalidInput);

pub fn raw_os_error(&self) -> Option<i32>[src]

Returns the OS error that this error represents (if any).

If this Error was constructed via last_os_error or
from_raw_os_error, then this function will return Some, otherwise
it will return None.

use std::io::{Error, ErrorKind};

fn print_os_error(err: &Error) {
    if let Some(raw_os_err) = err.raw_os_error() {
        println!("raw OS error: {:?}", raw_os_err);
    } else {
        println!("Not an OS error");
    }
}

fn main() {
    
    print_os_error(&Error::last_os_error());
    
    print_os_error(&Error::new(ErrorKind::Other, "oh no!"));
}

pub fn get_ref(&self) -> Option<&(dyn Error + 'static + Sync + Send)>1.3.0[src]

Returns a reference to the inner error wrapped by this error (if any).

If this Error was constructed via new then this function will
return Some, otherwise it will return None.

use std::io::{Error, ErrorKind};

fn print_error(err: &Error) {
    if let Some(inner_err) = err.get_ref() {
        println!("Inner error: {:?}", inner_err);
    } else {
        println!("No inner error");
    }
}

fn main() {
    
    print_error(&Error::last_os_error());
    
    print_error(&Error::new(ErrorKind::Other, "oh no!"));
}

pub fn get_mut(&mut self) -> Option<&mut (dyn Error + 'static + Sync + Send)>1.3.0[src]

Returns a mutable reference to the inner error wrapped by this error
(if any).

If this Error was constructed via new then this function will
return Some, otherwise it will return None.

use std::io::{Error, ErrorKind};
use std::{error, fmt};
use std::fmt::Display;

#[derive(Debug)]
struct MyError {
    v: String,
}

impl MyError {
    fn new() -> MyError {
        MyError {
            v: "oh no!".to_string()
        }
    }

    fn change_message(&mut self, new_message: &str) {
        self.v = new_message.to_string();
    }
}

impl error::Error for MyError {
    fn description(&self) -> &str { &self.v }
}

impl Display for MyError {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "MyError: {}", &self.v)
    }
}

fn change_error(mut err: Error) -> Error {
    if let Some(inner_err) = err.get_mut() {
        inner_err.downcast_mut::<MyError>().unwrap().change_message("I've been changed!");
    }
    err
}

fn print_error(err: &Error) {
    if let Some(inner_err) = err.get_ref() {
        println!("Inner error: {}", inner_err);
    } else {
        println!("No inner error");
    }
}

fn main() {
    
    print_error(&change_error(Error::last_os_error()));
    
    print_error(&change_error(Error::new(ErrorKind::Other, MyError::new())));
}

pub fn into_inner(self) -> Option<Box<dyn Error + 'static + Sync + Send>>1.3.0[src]

Consumes the Error, returning its inner error (if any).

If this Error was constructed via new then this function will
return Some, otherwise it will return None.

use std::io::{Error, ErrorKind};

fn print_error(err: Error) {
    if let Some(inner_err) = err.into_inner() {
        println!("Inner error: {}", inner_err);
    } else {
        println!("No inner error");
    }
}

fn main() {
    
    print_error(Error::last_os_error());
    
    print_error(Error::new(ErrorKind::Other, "oh no!"));
}

pub fn kind(&self) -> ErrorKind[src]

Returns the corresponding ErrorKind for this error.

use std::io::{Error, ErrorKind};

fn print_error(err: Error) {
    println!("{:?}", err.kind());
}

fn main() {
    
    print_error(Error::last_os_error());
    
    print_error(Error::new(ErrorKind::AddrInUse, "oh no!"));
}

impl<T> ToString for T where
    T: Display + ?Sized, 
[src]

impl<T, U> Into<U> for T where
    U: From<T>, 
[src]

fn into(self) -> U[src]

Performs the conversion.

impl<T> From<T> for T[src]

fn from(t: T) -> T[src]

Performs the conversion.

impl<T, U> TryFrom<U> for T where
    U: Into<T>, 
[src]

impl<T, U> TryInto<U> for T where
    U: TryFrom<T>, 
[src]

impl<T> BorrowMut<T> for T where
    T: ?Sized, 
[src]

impl<T> Borrow<T> for T where
    T: ?Sized, 
[src]

impl<T> Any for T where
    T: 'static + ?Sized, 
[src]

Понравилась статья? Поделить с друзьями:
  • Std error python
  • Std error of mean это
  • Std error handle
  • Std domain error
  • Stc profile false как исправить