Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the ? operator report the error "the trait bound NoneError: Error is not satisfied"?

The ? operator at line 9 works OK, but if I use the same logic on the same type in line 19, it blows up.

use std::error::Error;
use walkdir::WalkDir;

fn main() -> Result<(), Box<dyn Error>> {
    let valid_entries = WalkDir::new("/tmp")
        .into_iter()
        .flat_map(|e| e)
        .flat_map(|e| {
            let name = e.file_name().to_str()?; // <-- this works
            if name.contains(".txt") {
                Some(e)
            } else {
                None
            }
        });

    for entry in valid_entries {
        println!("This file matches: {:?}", entry);
        let name_to_str = entry.file_name().to_str()?; // <-- this blows up
        // ...
    }
    Ok(())
}

The errors are a little cryptic for me to interpret:

error[E0277]: the trait bound `std::option::NoneError: std::error::Error` is not satisfied
  --> src/main.rs:19:53
   |
26 |         let name_to_str = entry.file_name().to_str()?;
   |                                                     ^ the trait `std::error::Error` is not implemented for `std::option::NoneError`
   |
   = note: required because of the requirements on the impl of `std::convert::From<std::option::NoneError>` for `std::boxed::Box<dyn std::error::Error>`
   = note: required by `std::convert::From::from`

Why is the ? operator blowing up while iterating valid_entries?

like image 437
struggling_learner Avatar asked Jan 02 '20 18:01

struggling_learner


1 Answers

The ? can be used to check-and-return any type that implements the Try trait (still unstable). The only implementations in std of those are Option<T> and Result<T, E> (plus some Future-related impls that are not relevant to this discussion). This means that you can use the ? operator in any function that returns Result<T, E> or Option<T>.

But you cannot mix-n-match those. That is, if your function returns a Result<T, E> you cannot use the ? in a value of type Option<T>. Or vice versa.

The reason your first ? works is because you are inside a flat_map() that returns Option<String> and all goes well. The second one, however, is in a function that returns a Result<(), Box<dyn Error>> so you can't use ? with an Option<String>.

The solution is simply to deal with the None in your Option<String> in another way:

  1. Doing a match / if let Some(x) to handle the error separately.
  2. Converting into a Result<String, Error> and use ?, for example with .ok_or(std::io::ErrorKind::InvalidData)?;.
  3. Similar to 2, but take advantage of the impl From<&str> for Box<dyn Error> and do .ok_or("invalid file name").
  4. Giving a default value, with Option::unwrap_or() or similar.
  5. Doing unwrap() and panicking if None.

Ok, but what does this error mean? The idea is that actually you are able to use ? with an Option<T> that returns a Result<T, E>, as long as your E implements From<std::option::NoneError>. Unfortunately, NoneError is still unstable, too, so you can't implement From<NoneError> in your code using the stable compiler. Nevertheless, the E in your code is Box<dyn Error>, and the compiler would be happy to do the boxing as long as NoneError implements Error, but...

error[E0277]: the trait bound `std::option::NoneError: std::error::Error` is not satisfied
like image 90
rodrigo Avatar answered Nov 19 '22 20:11

rodrigo