Rust `From` trait, errors, reference vs Box and `?` operator [duplicate] - error-handling

This question already has answers here:
Is there any way to return a reference to a variable created in a function?
(5 answers)
Closed 3 years ago.
I am pretty confused on the ? operator in functions that return Result<T, E>.
I have the following snippet of code:
use std::error;
use std::fs;
fn foo(s: &str) -> Result<&str, Box<error::Error>> {
let result = fs::read_to_string(s)?;
return Ok(&result);
}
fn bar(s: &str) -> Result<&str, &dyn error::Error> {
// the trait `std::convert::From<std::io::Error>` is not implemented for `&dyn std::error::Error` (1)
let result = fs::read_to_string(s)?;
return Ok(&result);
}
fn main() {
println!("{}", foo("foo.txt").unwrap());
println!("{}", bar("bar.txt").unwrap());
}
As you might see from the above snippet, the ? operator works pretty well with the returned boxed error, but not with dynamic error references (error at (1)).
Is there any specific reason why it does not work? In my limited knowledge of Rust, it is more natural to return an error reference, rather than a boxed object: in the end, after returning rom the foo function, I expect deref coercion to work with it, so why not returning the error reference itself?

Look at this function signature:
fn bar(s: &str) -> Result<&str, &dyn error::Error> {
The error type is a reference, but a reference to what? Who owns the value being referenced? The value cannot be owned by the function itself because it would go out of scope and Rust, quite rightly, won't allow you to return the dangling reference. So the only alternative is that the error is the input string slice s, or some sub-slice of it. This is definitely not what you wanted.
Now, the error:
the trait `std::convert::From<std::io::Error>` is not implemented for `&dyn std::error::Error`
The trait isn't implemented, and it can't be. To see why, try to implement it by hand:
impl<'a> From<io::Error> for &'a dyn error::Error {
fn from(e: io::Error) -> &'a dyn error::Error {
// what can go here?
}
}
This method is impossible to implement, for exactly the same reason.
Why does it work for Box<dyn Error>? A Box allocates its data on the heap, but also owns that data and deallocates it when the box goes out of scope. This is completely different from references, where the owner is separate, and the reference is prevented from outliving the data by lifetime parameters in the types.
See also:
Is there any way to return a reference to a variable created in a function?

Although it is possible to cast the concrete type std::io::Error into dyn Error, it is not possible to return it as a reference because the "owned" value is being dropped/erased/removed at the end of the function, same goes to your String -> &str. The Box<error::Error> example works because an owned Error is created in the heap (Box<std::io::Error>) and the std has an implementation of Error for Box<T> (impl<T: Error> Error for Box<T>).
If you want to erase the concrete type and only work with the available methods of a trait, it is possible to use impl Trait.
use std::{error, fs};
fn foo(s: &str) -> Result<String, Box<dyn error::Error>> {
let result = fs::read_to_string(s)?;
Ok(result)
}
fn bar(s: &str) -> Result<String, impl error::Error> {
let result = match fs::read_to_string(s) {
Ok(x) => x,
Err(x) => return Err(x),
};
Ok(result)
}
fn main() {
println!("{}", foo("foo.txt").unwrap());
println!("{}", bar("bar.txt").unwrap());
}

Related

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

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?
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)

How does the ? operator interact with the From trait?

Say I have the following:
use std::fs::File;
impl From<i32> for Blah {
fn from(b:i32) -> Blah {
Blah {}
}
}
fn main() {}
enum MyError {
ParseError,
}
impl From<std::io::Error> for MyError {
fn from(_:std::io::Error) -> Self {
MyError::ParseError
}
}
fn get_result() -> Result<Blah, MyError> {
let mut file = File::create("foo.txt")?;
}
This compiles fine. I don't understand how.
File::create throws an std::io::error, which we're trying to wrap in a MyError. But we never explicitly call from anywhere!? How does it compile?
As the comments from this answer Rust understanding From trait indicate, you do have to explicitly call from.
So, how is the above snippet compiling?
The difference is stated in The Rust Programming Language, chapter 9, section 2, when talking about the ? operator:
Error values that have the ? operator called on them go through the from function, defined in the From trait in the standard library, which is used to convert errors from one type into another. When the ? operator calls the from function, the error type received is converted into the error type defined in the return type of the current function. This is useful when a function returns one error type to represent all the ways a function might fail, even if parts might fail for many different reasons. As long as each error type implements the from function to define how to convert itself to the returned error type, the ? operator takes care of the conversion automatically.
You have provided this implementation of From<std::io::Error> for that error type, therefore the code will work and convert values of this type automatically.
The magic is in the ? operator.
let mut file = File::create("foo.txt")?;
expands to something like (source)
let mut file = match File::create("foo.txt") {
Ok(t) => t,
Err(e) => return Err(e.into()),
};
This uses the Into trait, which is the counterpart to the From trait: e.into() is equivalent to T::from(e). Here you have the explicit conversion.
(There is an automatic impl<T, U> Into<U> for T for every impl<T, U> From<T> for U, which is why implementing From is enough.)

Is it possible to cast a trait object to another trait object? [duplicate]

This question already has answers here:
Why doesn't Rust support trait object upcasting?
(5 answers)
Closed 2 years ago.
I tried the following code:
trait TraitA {
fn say_hello(&self) {
self.say_hello_from_a();
}
fn say_hello_from_a(&self);
}
trait TraitB {
fn say_hello(&self) {
self.say_hello_from_b();
}
fn say_hello_from_b(&self);
}
struct MyType {}
impl TraitA for MyType {
fn say_hello_from_a(&self) {
println!("Hello from A");
}
}
impl TraitB for MyType {
fn say_hello_from_b(&self) {
println!("Hello from B");
}
}
fn main() {
let a: Box<dyn TraitA> = Box::new(MyType {});
let b: Box<dyn TraitB>;
a.say_hello();
b = a;
b.say_hello();
}
I get the following compilation error:
error[E0308]: mismatched types
--> src/main.rs:34:9
|
34 | b = a;
| ^ expected trait `TraitB`, found trait `TraitA`
|
= note: expected struct `std::boxed::Box<dyn TraitB>`
found struct `std::boxed::Box<dyn TraitA>`
I declared two traits and a type called MyType and implemented both traits for MyType. I created a new trait object TraitA of type MyType which I called a. Since a also implements TraitB, I thought it should be able to be casted as TraitB.
I haven't figured out if it's even possible. If it is, how can I cast trait object a into TraitB?
In C++, I would use something similar to std::dynamic_pointer_cast<TraitB>(a); for the same purpose.
Here's an example of a case where I could use lateral casting: I have a struct with some data inside that represents some real life entity:
struct MyType {
a: i32,
b: i32,
}
Instances of this type can be used in at least two different parts of the code base. On both parts I need a behavior called get_final_value.
The interesting part is that get_final_value should respond differently depending on who called it.
Why don't I split the type into two different ones?: Technically, by design, a and b belong together, not to say that get_final_value() uses both values to compute the result.
Why not use generics/static dispatch? Because MyType is just one example. In the real case I have different structs, all of them implementing both traits in different ways.
Why not use Any trait? To be honest, I didn't know of it's existence until recently. I don't recall The Rust Programming Language mentioning it. Anyway, it seems you need to know the concrete type to do a cast from Any to that concrete type and then to the trait object.
Another option is to create a trait that uses both TraitA and TraitB as supertraits and provides a cast to each type:
trait TraitC: TraitA + TraitB {
fn as_trait_a(&self) -> &dyn TraitA;
fn as_trait_b(&self) -> &dyn TraitB;
}
Then have MyType implement it:
impl TraitC for MyType {
fn as_trait_a(&self) -> &dyn TraitA {
self
}
fn as_trait_b(&self) -> &dyn TraitB {
self
}
}
Once you do that, you can use TraitC for your Box and your program logic that uses both TraitA and TraitB together.
Sample main to show various ways to use:
fn test_a(a: &TraitA) {
a.say_hello();
}
fn test_b(b: &TraitB) {
b.say_hello();
}
fn main() {
let c: Box<dyn TraitC> = Box::new(MyType {});
TraitA::say_hello(&*c);
TraitB::say_hello(&*c);
c.as_trait_a().say_hello();
c.as_trait_b().say_hello();
test_a(c.as_trait_a());
test_b(c.as_trait_b());
let a: &dyn TraitA = c.as_trait_a();
a.say_hello();
let b: &dyn TraitB = c.as_trait_b();
b.say_hello();
}
Rust Playground
If A and B do truly belong together, this better represents that and still gives you the freedom to use them separately if you desire.
Using Box<MyType> instead of Box<dyn Trait> solves this problem.
fn main() {
let a = Box::new(MyType {});
TraitA::say_hello(&*a);
TraitB::say_hello(&*a);
}
In this case there's no need to use trait objects. Rust has a different paradigm from C++. In most cases you can usually use generic types to solve problems.
If your problem is really suitable to solve with trait objects, you can refer to the OOP chapter in the book.

Trying to implement an iterator: cannot infer an appropriate lifetime due to conflicting requirements

I'm trying to implement an iterator on my own struct. My general approach is by generating and storing an iterator the first time next is invoked, and then calling this iterator each time I need a value.
My minimal failing example looks like this, and the heart of it is:
if !self.vals.is_some() {
self.vals = Some(Box::new({
self.display.chars().filter(|&i| i == self.look_for)
}) as Box<std::iter::Iterator<Item = _>>);
}
My code fails to compile, producing the following message:
help: consider using an explicit lifetime parameter as shown: fn next(self: &'a mut Self) -> Option<<Self>::Item>
Following the advice doesn't help (just leads to more compile errors saying that my implementation is incompatible with the Iterator trait definition.
I'd appreciate help understanding what's going wrong and how I can fix it.
The problem is that the closure you pass to filter needs to borrow self, but you can't store a reference to self in the struct itself.
In this case, we can work around it by storing a copy of the value in the closure instead. This is done in two steps:
Assign self.look_for to a local variable, and use the local variable in the closure instead. This way, the closure is not tied to self.
Add move to the closure. The closure will thus capture the local variable by value.
Here's the final code:
impl<'a> Iterator for StatefulCounter<'a> {
type Item = bool;
fn next(&mut self) -> Option<Self::Item> {
if !self.vals.is_some() {
let look_for = self.look_for;
self.vals = Some(Box::new({
self.display.chars().filter(move |&i| i == look_for)
}));
}
if let &Some(v) = &self.vals.as_mut().unwrap().next() {
Some(expensive(v))
} else {
None
}
}
}
The explicit cast on the Box is not necessary, so I removed it.

Providing an implementation when both trait and type are not in this crate [duplicate]

This question already has answers here:
How do I implement a trait I don't own for a type I don't own?
(3 answers)
Closed 7 years ago.
I want to provide an implementation of a trait ToHex (not defined by me, from serialize) for a primitive type u8:
impl ToHex for u8 {
fn to_hex(&self) -> String {
self.to_str_radix(16)
}
}
The problem is I get this compiler error:
error: cannot provide an extension implementation where both trait and type are not defined in this crate
I understand the reason of this error and its logic, this is because both the trait and the primitive type are external to my code. But how can I handle this situation and provide an ToHex implementation for u8? And more generally how do you handle this kind of issue, it seems to me that this problem must be common and it should be possible and easy to extend types like this?
You should use a newtype struct to do this:
pub struct U8(pub u8)
impl ToHex for U8 {
fn to_hex(&self) -> String {
let U8(x) = *self;
x.to_str_radix(16)
}
}
This does mean, however, that you should wrap u8 into U8 where you need to perform this conversion:
let x: u8 = 127u8
// println!("{}", x.to_hex()); // does not compile
println!("{}", U8(x).to_hex());
This is absolutely free in terms of performance.
I realize this is almost a year old, but the answer was never accepted and I think I've found an alternate solution, that I thought would be good to document here.
In order to extend the functionality of the u8 through traits, instead of trying to extend ToHex, why not create a new trait?
trait MyToHex {
fn to_hex(&self) -> String;
}
impl MyToHex for u8 {
fn to_hex(&self) -> String {
format!("{:x}", *self)
}
}
then used like so
fn main() {
println!("{}", (16).to_hex());
}
This has the advantage that you don't have to wrap every u8 variable with a new and superfluous data type.
The disadvantage is that you still can't use a u8 in a external function (i.e std library, or one you have no control over) that requires the ToHex trait (Vladimir Matveev's solution works in this case), but from OP it sounds like all you want to do is extend u8 only inside your code.