Can `try!` and `?` use the `Into` trait instead of `From`?


#1

The Into trait has strictly more implementations, so it feels logical to use it instead of From.

I found the original PR that introduced try!, but that doesn’t have any rationale on why From was used instead of Into.


#2

From<T> for U implies Into<U> for T.

This means that the two traits should have the same number of implementaion.


#3

No, that means for all U: From<T>, T: Into<U>, i.e. Into<U> is the strictly larger set.

There is a case where it is impossible to implement From and an Into impl is required: (from Into docs)

There is one exception to implementing Into. If the destination type is not part of the current crate, and it uses a generic variable, then you can’t implement From directly.

Since Into is the strictly larger case, I agree that it should be used. It also aligns with typical usage of the traits: when you want a thing that needs to turn into U you take a T: Into<U> not a T where U: From<T>.


#4

Apparently this was tried before, and found to break things: https://github.com/rust-lang/rfcs/pull/1859#issuecomment-299855451


#5

Thanks for the link! For posterity, I believe that comment refers to @bluss’s comment at https://github.com/rust-lang/rfcs/pull/1859#issuecomment-282503600

Though that doesn’t tell me why using Into won’t work – just that someone had tried it and failed.

@bluss do you remember what errors you encountered while switching the desugaring of ? to use Into?


#6

I don’t remember the exact errors (best would be to reproduce it), but I think it was related to nested use of ? in some code found in the compiler. Imagine using ? in a closure and then ? on the result of the closure.


#7

Type inference problems related to .into().into() perhaps?


#8

Why theorize when we can just ask the compiler?


I tried modifying this single line in hir/lowering.rs.

When it compiles librustc, it produces a single error:

error[E0282]: type annotations needed
   --> librustc/util/ppaux.rs:263:29
    |
263 |           let fn_trait_kind = ty::tls::with(|tcx| {
    |  _____________________________^
264 | |             // Unfortunately, some kinds of items (e.g., closures) don't have
265 | |             // generics. So walk back up the find the closest parent that DOES
266 | |             // have them.
...   |
338 | |             Ok(tcx.lang_items().fn_trait_kind(path_def_id))
339 | |         })?;
    | |___________^ cannot infer type for `_`

Permalink to relevant code.


Imagine using ? in a closure and then ? on the result of the closure.

This is indeed what happens in that function. I am genuinely surprised, though, as I thought this was categorically impossible to do without type annotations… (unless you have a regular return result somewhere in the closure, where result's error type is known);


#9

Thanks for trying it out!

Inspired by your findings, I wrote up a simple test case (playground):

macro_rules! custom_try {
    ($x:expr) => {
        match $x {
            Ok(r) => r,
            Err(e) => return Err(From::from(e)),
            // Err(e) => return Err(Into::into(e)),
        }
    };
}

#[derive(Debug)]
struct Homura;

fn soul_gem<F>(callback: F) -> Result<(), Homura> where
    F: FnOnce() -> Result<(), Homura>,
{
    callback()
}

fn contract() -> Result<(), Homura> {
    soul_gem(|| Ok(custom_try!(Ok(()))))
}

fn main() {
    println!("{:?}", contract());
}

Change the From::from to Into::into, and the code no longer compiles.


#10

And adding the following also breaks it:

struct Akemi;
impl from<Akemi> for Homura {
    fn from(_: Akemi) -> Homura { Homura }
}

So basically, this can only be done where the Error type has no From impls (aside from the generic identity impl).


#11

Necro’ing! Is this something that would be valuable to try to change in the new edition?

(It’s a lowering change so potentially could be done, though I suspect warning about it & rustfix’ing it would be hard.)

Edit: Opened https://github.com/rust-lang/rust/issues/49531


#12

Maybe? File an issue, perhaps? I’d like to get a better understanding of what breaks.