Pre RFC: Remove `FromError` trait, add `From` trait

The ability to define how external types can be transformed into internal types is very powerful. With a few impls, the readability of error handling has been improved drastically. However, there are other areas that would benefit from this flexibility (event handling comes to mind). My hope is to remove the FromError trait and add a From trait. The only change between the two would be the name.

Let me know what you think! If there’s good feedback, I’ll write up an RFC!

Edit: Looking into the standard library, we also have FromPrimitive and FromString traits. Could these be replaced by a general From trait?

5 Likes

I was thinking the same thing this weekend, that there’s no particular reason this trait needs to be confined to Errors.

It would be nice if this could be used instead of FromPrimitive, ToPrimitive, FromStr, etc. However, those methods return the result wrapped in an Option, but FromError doesn’t. Should the generic From trait wrap the return value in an Option, or not? If not, it would mean that this couldn’t be used as a replacement for FromPrimitive etc.

I have some code in a linear algebra library that would be greatly simplified by something like this. I have a Vector3<T> struct and I sometimes need to convert between different T. AFAIK, it’s not currently easy to write a generic method to do the conversion, and it sounds like this proposal could help.

Good point @nstoddard. Maybe two traits then? From and MaybeFrom?

Would you need MaybeFrom? What’s wrong with just implementing the trait on Option<T>, for example:

trait From<T> {
    fn from(x: T) -> Self;
}

impl<'a> From<&'a str> for Option<i64> {
    fn from(x: &str) -> Option<i64> {
        Some(1234)
    }
}

@tomjakubowski: yep, that’s way better.

Replacing FromPrimitive with a generic From trait could be problematic because FromPrimitive has two required methods (for converting from u64 and i64 respectively) and a whole bunch of default methods for converting from the other primitive types. We could create blanket impls:

impl<T> From<u32> for T where T: From<u64> {
    fn from(x: u32) -> T { From::from(x as u64) }
}

But then you can’t do the equivalent of overriding FromPrimitive's from_u32 default method because of conflicting impls.

Another great usecase for From: Genericizing over smart pointers!

http://is.gd/ZSgrLG

I’m not 100% sure that this is a good idea. The purpose of a trait is not to require that a type has a method with a certain name with a certain set of argument types with a certain return type. The purpose of a trait is to require that a type has a method which does a certain thing. Generalising all From traits (FromError, FromPrimitive, FromStr, FromIterator, BorrowFrom) into a single trait will lose the extra requirements and invariants that must be obeyed that come with the trait, making a trait whose signature is all that matters, and the actual purpose of the function left undefined.

But to be honest I can’t think of a concrete example of any trait’s connotations that would be lost by a generalisation to a single From trait. So I’m going to say that this is a neat idea, and could be made even cooler if it were a special lang item trait that actually controlled the as operator. That would mean that you could convert a String to a &str with as ("foo".to_string() as &str), and maybe even convert them the other way round (perhaps with to and into operators instead).

1 Like

@Gankra: you are the guy that has been doing all the collections reform right? Would you be willing to write this RFC or help me write it?

I don’t have a ton of time to be writing up RFCs right now, but I’m willing to provide help.

Also I pseudo-retract the SmartPtr point. If you know you need some concrete contents like int, then you can use Smaht, but if you just want to take a Smaht and then use it with multiple types (or just have your interface hide which types are needed), then you probably needed HKT or something.

That sounds very reasonable to do and will standardize an important concept (generic conversions).

Strong -1 on this. I wrote a bunch of FromSomething traits already and they have vastly diverging requirements to the signature. For instance FromError in rust-welder changed the situation to include error location information. FromRedisValue in redis-rs has more than one method, FromSQL in Postgres has more than one parameter (postgres type + option value).

I can’t see how this would help anything.

As an example for rust-welder: This is how the FromError trait looks in there:

pub trait FromError<E> {
    fn from_error(err: E, loc: Option<ErrorLocation>) -> Self;
}

@mitsuhiko: I don’t see why that example couldn’t be rewritten as

impl From<(PostgresError, Option<ErrorLocation>)> for MyOtherErrorType {
    fn from(o: (PostgresError, Option<ErrorLocation>)) -> MyOtherErrorType {
        ...
    }
}

You could very easily have “arguments” as tuples of values.

I don’t think this is good enough. Although I don’t see why we shouldn’t have the 2 most common (From and MaybeFrom) in the stdlib.

@arthurprs: I don’t see what would be so bad with

impl From<&str> for Option<uint> {
    ...
}

I was actually referring to the tuple parameter version.

The tuple solution is both looking ugly and not solving the problem where you need more than one method on the trait. So you will still see other FromX traits showing up. What’s the point of a generic From trait then?

The point would be that in 90% of cases, converting between one type to another is good enough. Other languages have language features (overridable casting) to get exactly this.