# Standardize on From/Into for all conversions

All conversions, especially those of TryFrom and Try, should use From/Into. E.g.

``````pub trait MyTry where Self: Into<Result<<Self as MyTry>::Ok, <Self as MyTry>::Error>>, <Self as MyTry>::Ok: Into<Self> {
type Ok;
type Error;
fn from_error(e: <Self as MyTry>::Error) -> Self;
}

impl<T, E> MyTry for Result<T, E> where E: Into<Self>, T: Into<Self> {
type Ok = T;
type Error = E;
fn from_error(e: E) -> Self {
Err(e)
}
}

#[derive(Eq, PartialEq)]
struct Foo;

// this should be impl<T> From<T> for Result<T, E>
impl<E> Into<Result<Foo, E>> for Foo {
fn into(self) -> Result<Foo, E> {
Ok(self)
}
}

fn main() {
let o: Result<Foo, Foo> = Foo.into();
let e: Result<Foo, Foo> = MyTry::from_error(Foo);
assert!(o != e);
}
``````

(note that `from_error` is, sadly, still necessary. but also note that we donâ€™t need into_result or from_ok, as those can be done with From/Into! this provides a more generic stdlib, so using it is easier! also note that we automatically get a TryFrom - From for Result<T, E> is equivalent to but better than TryFrom for T!)

But TryFrom can fail, and From canâ€™t.

They are fundamentally different operations.

If I have a guaranteed operation. I donâ€™t want to handle errors. and if I have an error case I need to.

2 Likes

`From<U> for Result<T, E>` can fail.

e.g.

``````impl From<u16> for Result<u8, !> {
fn From(v: u16) -> Self {
if v > 255 { panic!(); }
Ok(mem::transmute::<[u8;2]>(v)[0])
}
}
``````

I use From/Into all the time, let me use it more.

Small note: I think the title of this thread sounds a bit misleading; it sounds like youâ€™re saying we should get rid of `TryInto` and just use `Into<T>`; but after reading it I think I see you are suggesting that trait methods like `Try::from_ok` and `Try::into_result` should not exist and ought to be provided through `Into`.

Suggestion: change â€śall conversionsâ€ť to â€śall conversions in traits?â€ť

Iâ€™m against this for the same reasoning I was against using `Into<Option<T>>` for an operator in another thread.

Using one trait (`Into`) for a large multitude of purposes makes the language less powerful. We will run into cases where we are forced to choose between one feature or another for our type because the `Into` impls we want to write would overlap.

1 Like

I believe the general-ness of Into actually makes the language more powerful, even if it could restrict what impls you may use. I use the term â€ścouldâ€ť here, because I believe newtypes solve that problem.

In the general case, these impls would provide great ergonomics/convenience for most operations. In the few edge cases where they donâ€™t, youâ€™d be able to use newtypes and it would no longer be a big deal.

â€śMake simple things simple and complex things possibleâ€ť - someone

My gut reaction is that needing to wrap something in a newtype to use some functionality can be unergonomic for consumers of a type---especially if these values may be inside a container. (e.g. changing `Vec<T>` to `Vec<MyNewtype<T>>` requires allocation, and changing `&[T]` to `&[MyNewtype<T>]` requires `unsafe`)

Clearly, we disagree on how big of a problem overlap will be in practice. You don't believe it will happen often, whereas I fear it will happen all over the place. (As I see it, overlap happens quite often enough even when we aren't overloading the meaning of traits!).

The only way to know for sure is to try implementing it at a large scale.

I would like the nightly compiler to have "A/B testing" mode. But I don't think that's implemented yet.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.