wisha
wisha

Reputation: 713

Making a generic From/Into shortcut for two-steps From

StructA implements From<StructB>, and StructB implements From<S>.

How can I generically implement a 'shortcut' Into<StructA> for S or From<S> for StructA?

If this is a bad idea, please do tell. But kindly explain how to do it anyway for the sake of learning.

Here's my attempt:

struct StructA {
    f: StructB
}

struct StructB {
    g: i32
}

impl From<StructB> for StructA {
    fn from(v: StructB) -> Self {
        Self {
            f: v
        }
    }
}

impl From<i32> for StructB {
    fn from(v: i32) -> Self {
        Self {
            g: v
        }
    }
}

impl<T: Into<StructA>, S: Into<T>> Into<StructA> for S {
    fn into(self) -> StructA {
        let i: T = self.into();
        i.into()
    }
}

The error I get is the type parameter 'T' is not constrained by the impl trait, self type, or predicates.

I don't understand it. Isn't T constrained by Into<StructA>?

Upvotes: 2

Views: 111

Answers (3)

Dzejkop
Dzejkop

Reputation: 93

As far as I can tell this is impossible on stable Rust as it would require some form of specialization. Let's consider this code:

// "StructB -> StructA"
impl From<StructB> for StructA {
    fn from(v: StructB) -> Self {
        Self { f: v }
    }
}

impl From<i32> for StructB {
    fn from(v: i32) -> Self {
        Self { g: v }
    }
}

// "shortcut"
impl<T> From<T> for StructA
where
    StructB: From<T>,
{
    fn from(t: T) -> Self {
        Self::from(StructB::from(t))
    }
}

The compiler will complain that the "StructB -> StructA" impl conflicts with the "shortcut" impl. Which is true since From<T> has a blanket implementation for itself.

Now using Into will not help at all, since Into is reflexive. So even you if you get over the "not constrained" error you'll be back to conflicting implementations.

However! It is possible on nightly. With a little trickery using auto traits (here's a nice article about it), we can restrict the "shortcut" implementation to only cover types different than StructB.

playground link

Upvotes: 1

pretzelhammer
pretzelhammer

Reputation: 15105

There's no way to produce a totally generic solution, because even if you make it past the "unconstrained type parameter" errors you'll then run into "conflicting trait implementation" errors. This is as close as I was able to get, which allows converting StructB into StructA or converting any type which can convert into StructB into StructA:

#[derive(Eq, PartialEq, Debug)]
struct StructA {
    f: StructB
}

#[derive(Eq, PartialEq, Debug)]
struct StructB {
    g: i32
}

impl From<i32> for StructB {
    fn from(v: i32) -> Self {
        Self {
            g: v
        }
    }
}

impl<T> From<T> for StructA
where
    T: Into<StructB>
{
    fn from(t: T) -> StructA {
        let structb = t.into();
        StructA {
            f: structb
        }
    }
}

fn main() {
    let a1: StructA = StructB { g: 12 }.into();
    let a2: StructA = 12.into();
    assert_eq!(a1, a2);
}

playground

Upvotes: 1

Sven Marnach
Sven Marnach

Reputation: 601539

The error message basically states that the compiler has no way to infer what type T may be – it basically has to figure out whether any type T exists such that the given trait bounds T: Into<StructA>, S: Into<T> are satisfied, and this is not possible in Rust. One problem with this is, as stated in the comments, that there might be multiple types T satisfying the trait bounds, in which case the compiler can't determine which one to use.

In addition, the Into trait already has a blanket implementation in the standard library.

impl<T, U: From<T>> Into<U> for T;

There is no way for the compiler to guarantee that this impl doesn't overlap with your blanket impl, which would also make the implementation ambiguous.

I recommend you simply implement From<i32> for StructA explicitly. If you need many such implementations, macros could be useful.

Upvotes: 1

Related Questions