我有一个类型为C<T>
的容器,其中包含一个类型T
的对象。
struct C<T> {
insides: T
}
假设我有两种类型A
和B
,它们可以在使用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());
}
到目前为止一切都很好,一切都按预期编译和工作。
但这是一个相当自然的模式,适用于任何一对可转换类型,而不仅仅适用于A
和B
。因此,人们可能想要一个全面的特性实现:
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使用core
中T=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));
}
游乐场
不要在生产中使用它,尤其是因为它可能在任何时候都是固定的。