How to Return a Result with generic error
Asked Answered
W

1

17

I want to write a function that reads the contents of a file, and raises an error if it fails. I want to call this function from a python script, so I'm including some mentions of Python below in case it might be relevant.

As I have tried showing in the comments, more work might happen that raises other types of errors, so if it is possible I would like to use a generic error if this is possible in Rust(?). How can I return the error so it can be handled and wrapped in a python error as shown in do_work? Not sure if my approach that is resulting in the error below is in the right direction.

fn work_with_text() -> Result<(), dyn std::error::Error> {
    let content = match std::fs::read_to_string("text.txt") {
        Ok(t) => t,
        Err(e) => return Err(e),
    };
    // do something with content that may cause another type of error (rusqlite error)
    Ok(())
}
    

#[pyfunction]
fn do_work(_py: Python) -> PyResult<u32> {
    match work_with_text() {
        Ok(_) => (0),
        Err(e) => {
            let gil = Python::acquire_gil();
            let py = gil.python();
            let error_message = format!("Error happened {}", e.to_string());
            PyIOError::new_err(error_message).restore(py);
            return Err(PyErr::fetch(py));
        }
    };

    // ...
}

error:

1   | ...                   fn work_with_text() -> Result<(), dyn std::error::Error> {
    |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
    |
    = help: the trait `Sized` is not implemented for `(dyn std::error::Error + 'static)`
Weatherspoon answered 22/1, 2021 at 12:49 Comment(7)
Box<dyn Error>Excuse
Is there a particular reason, you want to keep the return type as Result<(), dyn std::error::Error> instead of Result<(), std::io::Error> or just std::io::Result<()>?Flocculant
@Flocculant because I might have other types of errors as well, that I don't think would be covered by std::io. For example rusqlite errorsWeatherspoon
@SimenRussnes In that case, it would be more idiomatic to make your own enum Error with variants for each kind of error. Instead of dyn std::error::Error.Flocculant
@Flocculant really depends whether it's a library or an application, and what's later done with the error, though. Box<dyn Error> is usually fine in an application, or here if they only raise a very generic exception at the python level.Havoc
Also FWIW your match is a complicated way of writing let content = read_to_string(...)?;, and the later will take care of conversions if available & necessary.Havoc
@Havoc To each their own of course. Personally, I never use Box<dyn Error>, regardless of library vs application. Every time I've used Box<dyn Error>, I've in the future had to rework code, when I needed to handle the individual error kinds. Dealing with Box<dyn Error> can be quite annoying.Flocculant
C
22

Your current version does not work because trait objects don't have a statically known size, which means the compiler doesn't know how much space to allocate for them on the stack, so you can't use unsized types as function arguments or return values unless you make them sized by putting them behind a pointer.

Fixed example:

fn work_with_text() -> Result<(), Box<dyn std::error::Error>> {
    let content = std::fs::read_to_string("text.txt")?;
    // do something with content that may cause another type of error (rusqlite error)
    Ok(())
}

Having a Box<dyn std::error::Error> also allows you to return a wide variety of errors from your function since most error types can be automatically converted into a Box<dyn std::error::Error> via the ? operator.

If you'd like to get a deeper understanding of both sizedness and error handling in Rust I highly recommend reading Sizedness in Rust and Error Handling in Rust.

Codpiece answered 22/1, 2021 at 13:8 Comment(1)
You didn't show how to actually create the error manually, when you want to match and print for example.Boccioni

© 2022 - 2024 — McMap. All rights reserved.