为两步 From 创建通用 From/Into 快捷方式

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

StructA 实现了 From<StructB>StructB 实现了 From<S>.

我如何通用地实现 'shortcut' Into<StructA> for SFrom<S> for StructA

如果这是个坏主意,请告诉我。但是为了学习,请解释一下如何做。

这是我的尝试:

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 type parameter 'T' is not constrained by the impl trait, self type, or predicates

我不明白。 T不是受Into<StructA>约束吗?

错误消息基本上表明编译器无法推断 T 可能是什么类型——它基本上必须弄清楚是否存在任何类型 T 使得给定的特征边界 T: Into<StructA>, S: Into<T> 得到满足,这在 Rust 中是不可能的。一个问题是,如评论中所述,可能有多种类型 T 满足特征界限,在这种情况下,编译器无法确定使用哪一种。

此外,Into 特征已经在标准库中全面实施。

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

编译器无法保证此 impl 不会与您的一揽子 impl 重叠,这也会使实现不明确。

我建议您直接明确实施 From<i32> for StructA。如果您需要许多这样的实现,宏可能会有用。

没有办法生成完全通用的解决方案,因为即使您解决了“不受约束的类型参数”错误,您也会 运行 陷入“特征实现冲突”错误。这是我所能得到的最接近的,它允许将 StructB 转换为 StructA 将任何可以转换为 StructB 的类型转换为 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

据我所知,这在稳定的 Rust 上是不可能的,因为它需要某种形式的专业化。让我们考虑一下这段代码:

// "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))
    }
}

编译器会抱怨 "StructB -> StructA" impl 与 "shortcut" impl 冲突。这是真的,因为 From<T> 本身有一个 blanket implementation

现在使用 Into 根本没有帮助,因为 Into 是自反的。因此,即使您克服了“不受约束”的错误,您也会回到相互冲突的实现中。

不过! 每晚都有可能。通过使用 auto traits (here's a nice article about it) 的小技巧,我们可以将“快捷方式”实现限制为仅涵盖不同于 StructB.

的类型

playground link