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.

1 Like

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

1 Like

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.


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