Why does the decorator pattern work for owned types, but causes a trait evaluation overflow (E0275) for references?

Viewed 82

While experimenting with the decorator design pattern in Rust, I came across what I believe may be a compiler error, but I am too new to the language to be confident.

I think that the following example code should not generate a recursive trait E0275 error.

A simple type that can be converted to an i64:

enum MyNumbers {
    Zero,
    One,
    Two,
}

impl From<MyNumbers> for i64 {
    fn from(n: MyNumbers) -> Self {
        match n {
            MyNumbers::Zero => 0,
            MyNumbers::One => 1,
            MyNumbers::Two => 2,
        }
    }
}

And here, a struct that might be used in a decorator:

struct MyWrapper<N> {
    n: N,
}

MyWrapper<N> can be converted to an i64 if N can be converted to i64.

impl<N> From<MyWrapper<N>> for i64
where
    N: Into<i64>,
{
    fn from(wrapper: MyWrapper<N>) -> Self {
        wrapper.n.into()
    }
}

Playground

This works as I expect.

Now I want to be able to construct an i64 from MyWrapper without consuming it. I change my From trait implementations to operate on references:

impl From<&MyNumbers> for i64 {
    fn from(n: &MyNumbers) -> Self {
        match n {
            MyNumbers::Zero => 0,
            MyNumbers::One => 1,
            MyNumbers::Two => 2,
        }
    }
}

impl<'a, N> From<&'a MyWrapper<N>> for i64
where
    &'a N: Into<i64>,
{
    fn from(wrapper: &'a MyWrapper<N>) -> Self {
        (&wrapper.n).into()
    }
}

Playground

But now...

error[E0275]: overflow evaluating the requirement `i64: From<&MyWrapper<_>>`
  --> src/main.rs:34:13
   |
34 |     let i = i64::from(&w);
   |             ^^^^^^^^^
   |
   = help: consider adding a `#![recursion_limit="256"]` attribute to your crate (`playground`)
   = note: required because of the requirements on the impl of `Into<i64>` for `&MyWrapper<_>`
   = note: required because of the requirements on the impl of `From<&MyWrapper<MyWrapper<_>>>` for `i64`
   = note: 126 redundant requirements hidden
   = note: required because of the requirements on the impl of `From<&MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<MyWrapper<_>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` for `i64`

Why was this fine for owned types, but not references?

Also, this really confuses me - No calls to i64::from() is fine - Playground
Call to different i64::from errors - but should not be evaluating my code!? Playground

fn main() {
    i64::from(32i32);
}
1 Answers

Compiler bug. https://github.com/rust-lang/rust/issues/37748

As a workaround, I have had to resort to #[derive(Copy)] to prevent my types from being consumed at each level of the conversion. Perhaps From/Into is poorly suited to this pattern.... Until the language matures a little more.

Related