.into() iteration

Hi there rust-genies,

I'm once again surprised about something rust doesn't do: it cannot use the From implementations over multiple structs. If I define three structs, A, B, and C, and create From implementations for A->B and B->C, rust doesn't know how to use into() to go directly from A to C. This is shown in the following example, which doesn't work.

Is there a way to do this? Other than to define an implementation for A -> C?

Thanks a lot for any idea.

struct A {
    i: i32
}

struct B {
    i: i32
}

impl From<A> for B {
    fn from(a: A) -> B{
        B{ i: a.i }
    }
}

#[derive(Debug)]
struct C {
    i: i32
}

impl From<B> for C {
    fn from(b: B) -> C {
        C{ i: b.i }
    }
}

fn main() {
    let a = A{i: 2};
    // This doesn't work
    // let c: C = a.into();
    
    // This works
    let c: C = B::from(a).into();
    println!("C is: {c:?}");
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `C: From<A>` is not satisfied
  --> src/main.rs:28:18
   |
28 |     let c: C = a.into();
   |                  ^^^^ the trait `From<A>` is not implemented for `C`
   |
   = help: the following implementations were found:
             <C as From<B>>
   = note: required because of the requirements on the impl of `Into<C>` for `A`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground` due to previous error

The reason this does not happen automatically is that it is ambiguous. What if there were two different choices for the middle type?

2 Likes

Wow - fastest response ever. Thanks a lot.

Yes, that might be ambiguous, but then rust could tell me so and abort with an error, no? Couldn't it at least try?

Adding a new From impl really shouldn't be a breaking change.

1 Like

The actual use-case is some enums that are three levels deep and have at least 5 items each. So creating 5 * 5 * 5 implementations for the root-level enum is a bit much...

You can use a macro to define them.

Unfortunately, there's no other way around.

1 Like

Some time ago, I’ve written a macro that writes these kinds of implementation. (I was planning to extend it, currently it only supports non-generic types in a tree-like hierarchy… well maybe that happens to be your use-case?)

See here: https://docs.rs/transitive_from/0.1/transitive_from/macro.hierarchy.html

1 Like

Wow - that looks fancy and exactly what I need. Will this also work with enums?

Certainly works with (non-generic) enums. E.g. look at this test case: lib.rs.html -- source

I would caution you against implementing such conversions via macros. You can easily blow up your compile times and generated code size. Do you really need 125 implementations? I would assume there are only a few conversions which you really need, and you can implement them by hand. Failing that, it is not a big deal to chain a couple of conversions by hand when you need them, there are likely just a few places where most of them are used anyway.

1 Like

To second afetisov's point, I've recently had a similar problem, and a good solution was to implement conversions for each link in A -> B -> ... -> J , and then one set of A -> J, B -> J, ..., I -> J. If you've got a 'root level enum', then you can try to make it a common interface, and minimize the functionality exposed by everything else. Otherwise, if there are that too many possible conversions, it might pay off just to be explicit and force each one manually.

(For context, these were expression AST nodes in a parse tree, and J is the top level Expr type, and the other values are AST nodes associated with varying precedence levels.)

From what I understand in @steffahn's response, with his macro I can define which branches I want to be created in the macro.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.