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


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.


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

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


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>.


Apparently this was tried before, and found to break things:


Thanks for the link! For posterity, I believe that comment refers to @bluss’s comment at

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?


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.


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


Why theorize when we can just ask the compiler?

I tried modifying this single line in hir/

When it compiles librustc, it produces a single error:

error[E0282]: type annotations needed
   --> librustc/util/
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);


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

struct Homura;

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

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.


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


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


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