Can you return a Result that works with any possible error type?

醉酒当歌 提交于 2020-01-14 07:04:25

问题


I want to use multiple libraries that each have their own error types. I don't really care about each specific crate's error type and I want to use the ? idiom to use the methods of those crates that return a Result type.

I don't want to unwrap the values either, that would cause a panic if it hits an error. I might just want to propagate the different errors using ? to the top and perhaps choose to deal with them or ignore them if I want.

I cannot do that with a std::result::Result<T, E> because I don't know the type of error returned (like I said, each crate could return its own errors).

I am aware that in Rust there is no "object-oriented" polymorphism, but there are trait objects. Since a trait object's size cannot be known at compile time, we must hide them behind some kind of pointer like & or Box<_>.

The base trait implemented by errors seems to be std::error::Error.

One thing I've seen is the fn foo() -> Result<Blah, Box<dyn Error>> strategy, which utilizes the concept of trait objects.

The problem with this strategy is none of the crates return a boxed error, which leads to the compiler complaining about the same.

An example use-case:

use native_tls::TlsConnector; // 0.2.3
use std::io::{Read, Write};
use std::net::TcpStream;

fn main() {
    match do_stuff() {
        Ok(string) => {
            println!("{}", string);
        }
        _ => {
            println!("Failed!");
        }
    }
}

fn do_stuff() -> Result<String, Box<(dyn std::error::Error + 'static)>> {
    let connector = TlsConnector::new()?;

    let stream = TcpStream::connect("jsonplaceholder.typicode.com:443")?;
    let mut stream = connector.connect("jsonplaceholder.typicode.com", stream)?;

    stream.write_all(b"GET /todos/1 HTTP/1.0\r\n\r\n")?;
    let mut res = vec![];
    stream.read_to_end(&mut res)?;
    String::from_utf8(res)
}

playground

Is there an easy way around this problem? Can I easily abstract away all the different errors and return a Result so I can use the ? idiom?


回答1:


Can you return a Result that works with any possible error type?

No, you cannot. On the surface, this cannot make sense. Generic types are chosen by the caller of the function, so how would a function create an error that was chosen by someone else, without being told how to construct it?


That said, your problem is easily solved. You said:

so I can use the ? idiom

If you do that consistently, your program compiles:

let s = String::from_utf8(res)?;
Ok(s)

You could also convert the error type directly:

String::from_utf8(res).map_err(Into::into)

none of the crates return a boxed error, which leads to the compiler complaining about the same

It does not for the 5 other cases where you've used ?, so it's unclear why you make this statement.

Specifically, Box<dyn Error> can be created from any type that implements Error:

impl<'a, E: Error + 'a> From<E> for Box<dyn Error + 'a> {
    fn from(err: E) -> Box<dyn Error + 'a> {
        Box::new(err)
    }
}

The ? operator calls From::from for you under the hood.

See also:

  • What is this question mark operator about?
  • How to manually return a Result<(), Box<dyn Error>>?
  • Rust proper error handling (auto convert from one error type to another with question mark)


来源:https://stackoverflow.com/questions/59584485/can-you-return-a-result-that-works-with-any-possible-error-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!