转换所包含类型的容器类型的Blanket"From"实现



我有一个类型为C<T>的容器,其中包含一个类型T的对象。

struct C<T> {
insides: T
}

假设我有两种类型AB,它们可以在使用From特性之间转换:

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

在我的情况下,使用这种转换,我应该能够很容易地将C<A>放在需要C<B>的任何地方,这是很自然的。这可以通过将From扩展到容器类型来实现:

impl From<C<A>> for C<B> {
fn from(ca: C<A>) -> C<B> {
C { insides: B::from(ca.insides) }
}
}

fn foo(cb: C<B>) {}
fn main() {
let ca = C { insides: A {} };
foo(ca.into());
}

到目前为止一切都很好,一切都按预期编译和工作。

但这是一个相当自然的模式,适用于任何一对可转换类型,而不仅仅适用于AB。因此,人们可能想要一个全面的特性实现:

impl<T, U> From<C<T>> for C<U> where U: From<T> {
fn from(t: C<T>) -> C<U> {
C { insides: U::from(t.insides) }
}
}

这不起作用:

error[E0119]: conflicting implementations of trait `std::convert::From<C<_>>` for type `C<_>`
--> src/main.rs:19:1
|
19 | impl<T, U> From<C<T>> for C<U> where U: From<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<T> From<T> for T;
For more information about this error, try `rustc --explain E0119`.

我理解错误:对于任何X,如果我们为T=X,U=X指定impl,我们就会发生冲突,因为X: From<X>代表所有X。但这是一个相当令人讨厌的边缘案例。有办法解决这个问题吗?以某种方式指定一个绑定语句T != U,或者以任何其他方式告诉Rust使用coreT=U的任何一揽子实现,以及我的impl用于其余部分?

在夜间,您可以使用自动特征:

#![feature(auto_traits, negative_impls)]
auto trait NotSame {}
impl<T> !NotSame for (T, T) {}
struct C<T> {
insides: T,
}
impl<T, U> From<C<T>> for C<U>
where
U: From<T>,
(T, U): NotSame,
{
fn from(t: C<T>) -> C<U> {
C {
insides: U::from(t.insides),
}
}
}

游乐场

然而,请注意,它可能有同样的专业化的健全性漏洞(当与寿命交互时(——我对此不确定。

然而,请注意,这实际上是(ab(使用了编译器错误-编译器不会分析并证明impl没有重叠,而是这些功能没有完全实现,因此编译器不会检查一致性。这意味着您需要更多的类型注释,也意味着您不能在std中使用impl<T> From<C<T>> for C<T> {},因为它与您的重叠:

#![feature(auto_traits, negative_impls)]
auto trait NotSame {}
// Need at least one negative impl
impl !NotSame for (i32, String) {}
#[derive(Debug)]
struct C<T> {
insides: T,
}
impl<T, U> From<C<T>> for C<U>
where
U: From<T>,
(T, U): NotSame,
{
fn from(t: C<T>) -> C<U> {
C {
insides: U::from(t.insides),
}
}
}
#[derive(Debug)]
struct A;
#[derive(Debug)]
struct B;
impl From<A> for B {
fn from(_: A) -> B { B }
}
fn main() {
let c: C<B> = C { insides: B };
dbg!(<C<B> as From<C<B>>>::from(c));
}

游乐场

不要在生产中使用它,尤其是因为它可能在任何时候都是固定的。

相关内容

最新更新