1. Some of the 3rd party error chain libraries help somehow but they don't help to remove the boiler plate when dealing with different types of error. I just want to be able to do:
fn func1() -> Result<MyStruct, Error1 | Error2> {
let foo = foo()?; // which can return Error1
let bar = bar()?; // which can return Error2
...
}
2. Error chain only shows the stack where I explicitly add it to the chain. Anything underneath is not shown. All these should be done by the runtime instead of forcing the developers to add code to handle it.
Also, error handling is so prevalent in Rust. If error chain is the way to go, make it as built-in. As right now, everyone has to hit a brick wall with Result, and then hunt around for the same solution.
3. It's the other way around &str to String, having to call .to_string() everywhere. Make it implicit and automatic if the type expects a String while a &str is passed in.
I completely agree that Rust ought to build in support for the error-chaining pattern. I think I'd still prefer to have a named type, but the standard library should provide a standard way to construct that type.
> 3. It's the other way around &str to String, having to call .to_string() everywhere. Make it implicit if the type expects a String while a &str is passed in.
You can't turn a reference like &str into an owned type like String without making a copy, and Rust doesn't do implicit copies (among many other reasons, because doing so would make it harder to notice code patterns that will lead to poor performance). So you'll always have to have some explicit indication that you want to make a copy.
In general, most functions should accept &str parameters rather than String, for exactly that reason; you should rarely run into functions that want a String. You can also use the "Cow" type if you want to support both owned and borrowed strings in the same structure.
> I think I'd still prefer to have a named type, but the standard library should provide a standard way to construct that type.
Having ad-hoc error union Result per function make it lightweight, and less friction in writing code. I would go one step further, let the compiler build the error union automatically.
fn func1() -> Result<MyStruct, _> {
let foo = foo()?; // might return Error1
let bar = bar()?; // might return Error2
...
}
The compiler infers the list of possible error types returning from the functions. Func1() would automatically have the return signature of Result<MyStruct, Error1 | Error2>.
That kind of automatic sum-typing seems like an interesting idea. You might consider bringing it to the Rust internals forum, posting it as a pre-RFC, and discussing it as compared to some of the alternatives. That might lead to either a change in the direction you hope for, or the unearthing of other ergonomic approaches.
This idea has been waved around a bit, but in the form of `impl Error` where `_` is. That is, inside the function the `E1 | E2 | ...` type is being built, and if you have automatic dispatch for `Error`'s methods then it will work with `impl Error`.
Actual global inference has never been on the table and still isn't.
I believe there are conversations of being able to promote enum variants to full-blown types, though I don't know if there's any project for lightweight enums of types.
I believe inferring sum types like that would make the inference system far more sensitive and complicated and possibly even slower. For one, it would likely mean mistakes like, say, assigning two different types to a variable may result in weird error messages, and may also limit how often coercions trigger.
I seem to run into functions that require String instead of &str all the time, most recently the "assert_eq!" macro which -- maybe I'm not understanding it correctly -- refuses to compare a &str to a String.
Okay, once again I try to find an example from my code, and once again it turns out that Rust makes it simple in the simple case, but the more complex cases are still confusing.
If I change the test value Some("zh".to_string()) into Some("zh"), it points to that line and tells me:
expected struct `std::string::String`, found &str
Sure, it's a different situation because the value is wrapped in an Option. But if String and &str are truly compatible, I would never expect to see that error message.
Ah yes. So this is an area where the diagnostic is _slightly_ misleading; it's trying to point out that you have two different types, and that that's the difference between the two of them. It's not that they can't be compared. Maybe a bug should be filed...
String and &str can normally be compared because of Deref, that is, &String derefs to &str. Option, on the other hand, does not implement Deref, and so no coercion happens. Rust doesn't do a lot of coercions, but Deref is one of the bigger ones.
Back to the _actual_ topic at hand, I can see how this can be a pain point until you know the rules, though. :/
> String and &str can normally be compared because of Deref, that is, &String derefs to &str. Option, on the other hand, does not implement Deref, and so no coercion happens.
For this particular case, any particular reason we couldn't add an impl of PartialEq? In fact, once we have specialization, couldn't we have a general impl of PartialEq for Options of Deref types?
> I think the issue is None. You'd get a null pointer, which doesn't make any sense in safe rust.
No, I don't mean an impl between Option<T> and T, I mean a bidirectional impl of PartialEq between Option<T> and Option<U> where U is T's Deref::Target.
In that case, None doesn't cause an issue; None == None, and Some(t) == Some(u) iff *t == u
Also, error handling is so prevalent in Rust. If error chain is the way to go, make it as built-in. As right now, everyone has to hit a brick wall with Result, and then hunt around for the same solution.
3. It's the other way around &str to String, having to call .to_string() everywhere. Make it implicit and automatic if the type expects a String while a &str is passed in.