Reputation: 713
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
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
.
Upvotes: 1
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);
}
Upvotes: 1
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