问题
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