为什么“From”不自动用于强制转换为特征实现类型
Why isn't `From` automatically used for coercing to trait implementing type
我有两个特点:
trait Foo {}
trait Bar {}
struct FooImpl;
impl Foo for FooImpl {}
struct BarImpl;
impl Bar for BarImpl {}
我想转换成的第三种类型:
struct Baz;
trait IntoBaz {
fn into(self) -> Baz;
}
由于连贯性,我无法为这两个特征定义两个 impl
的 IntoBaz
,所以我换一个:
struct FooWrapper<F>(F)
where
F: Sized;
impl<F: Foo + Sized> From<F> for FooWrapper<F> {
fn from(f: F) -> FooWrapper<F> {
FooWrapper(f)
}
}
impl<F: Foo + Sized> IntoBaz for FooWrapper<F> {
fn into(self) -> Baz {
Baz
}
}
我不包裹另一个:
impl<B: Bar> IntoBaz for B {
fn into(self) -> Baz {
Baz
}
}
fn do_thing<B: IntoBaz>(b: &B) {}
fn main() {
do_thing(&BarImpl);
}
到目前为止一切顺利,但为什么这条线不起作用?
fn main() {
do_thing(&FooImpl);
}
动机
我正在尝试向具有 fmt::Write
支持的库添加 io::Write
支持,而不引入重大更改。
最简单的方法是定义一些涵盖共享行为的内部 Write
特征,但一致性问题意味着我不能只将 From<io::Write>
实例写入内部特征。
我已经尝试包装 io::Write
个实例,使强制转换显式化,这样编译器会优先考虑较短的路径并避免不连贯,但它不会使用 From
实例自动强制转换.
查看报错信息:
error[E0277]: the trait bound `FooImpl: Bar` is not satisfied
--> src/main.rs:48:5
|
48 | do_thing(&FooImpl);
| ^^^^^^^^ the trait `Bar` is not implemented for `FooImpl`
|
= note: required because of the requirements on the impl of `IntoBaz` for `FooImpl`
note: required by `do_thing`
--> src/main.rs:45:1
|
45 | fn do_thing<B: IntoBaz>(b: &B) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
意思是说 FooImpl
没有 Bar
的实现,这是您全面 IntoBaz for B
实现的要求。
FooWrapper
实现不相关,因为 FooImpl
与 FooWrapper
不同。 From
和 Into
特征提供了一种在类型之间进行转换的方法,但它不会自动发生。
您可以尝试为可以转换为 FooWrapper
的事物添加一个实现,但这不会起作用,因为这些实现可能会重叠(并且 specialization 还不稳定)。
但是您可以为 FooImpl
:
定义一个 IntoBaz
实现
impl IntoBaz for FooImpl {
fn into(self) -> Baz {
IntoBaz::into(FooWrapper::from(self))
}
}
这将使您的代码编译:
fn main() {
do_thing(&BarImpl);
do_thing(&FooImpl);
}
所提问题完全正确。 From
和 Into
在类型级别上没有任何特殊意义。
但是,您可能只是问得太狭隘了。看起来您希望 do_thing(&BarImpl)
和 do_thing(&FooImpl)
编译并执行 "right" 操作。如果这就是您所需要的,那么有一个有点棘手的替代方法可以工作:向 IntoBaz
添加一个类型参数并使用不同的类型使 impl
不重叠。
trait IntoBaz<T> {
fn into_baz(self) -> Baz;
}
struct ByFoo;
impl<F: Foo> IntoBaz<ByFoo> for F {
fn into_baz(self) -> Baz {
Baz
}
}
struct ByBar;
impl<B: Bar> IntoBaz<ByBar> for B {
fn into_baz(self) -> Baz {
Baz
}
}
do_thing
现在可以通用于 T
:
fn do_thing<T, B: IntoBaz<T>>(_: &B) {}
调用时,如果只有一个T
有效,编译器会自动找到
fn main() {
do_thing(&BarImpl);
do_thing(&FooImpl);
}
I'm trying to add io::Write
support to a library with fmt::Write
support without introducing a breaking change.
不幸的是,这个建议在技术上是一个重大变化。如果有某种类型同时实现了 io::Write
和 fmt::Write
,那么 do_thing(&implements_both)
(过去使用 fmt::Write
)现在将失败由于歧义而编译。但是任何 trait 选择明确的地方仍然会像以前一样编译,所以破损的风险要低得多。
另请参阅:
- Can I avoid eager ambiguity resolution for trait implementations with generics?
我有两个特点:
trait Foo {}
trait Bar {}
struct FooImpl;
impl Foo for FooImpl {}
struct BarImpl;
impl Bar for BarImpl {}
我想转换成的第三种类型:
struct Baz;
trait IntoBaz {
fn into(self) -> Baz;
}
由于连贯性,我无法为这两个特征定义两个 impl
的 IntoBaz
,所以我换一个:
struct FooWrapper<F>(F)
where
F: Sized;
impl<F: Foo + Sized> From<F> for FooWrapper<F> {
fn from(f: F) -> FooWrapper<F> {
FooWrapper(f)
}
}
impl<F: Foo + Sized> IntoBaz for FooWrapper<F> {
fn into(self) -> Baz {
Baz
}
}
我不包裹另一个:
impl<B: Bar> IntoBaz for B {
fn into(self) -> Baz {
Baz
}
}
fn do_thing<B: IntoBaz>(b: &B) {}
fn main() {
do_thing(&BarImpl);
}
到目前为止一切顺利,但为什么这条线不起作用?
fn main() {
do_thing(&FooImpl);
}
动机
我正在尝试向具有 fmt::Write
支持的库添加 io::Write
支持,而不引入重大更改。
最简单的方法是定义一些涵盖共享行为的内部 Write
特征,但一致性问题意味着我不能只将 From<io::Write>
实例写入内部特征。
我已经尝试包装 io::Write
个实例,使强制转换显式化,这样编译器会优先考虑较短的路径并避免不连贯,但它不会使用 From
实例自动强制转换.
查看报错信息:
error[E0277]: the trait bound `FooImpl: Bar` is not satisfied
--> src/main.rs:48:5
|
48 | do_thing(&FooImpl);
| ^^^^^^^^ the trait `Bar` is not implemented for `FooImpl`
|
= note: required because of the requirements on the impl of `IntoBaz` for `FooImpl`
note: required by `do_thing`
--> src/main.rs:45:1
|
45 | fn do_thing<B: IntoBaz>(b: &B) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
意思是说 FooImpl
没有 Bar
的实现,这是您全面 IntoBaz for B
实现的要求。
FooWrapper
实现不相关,因为 FooImpl
与 FooWrapper
不同。 From
和 Into
特征提供了一种在类型之间进行转换的方法,但它不会自动发生。
您可以尝试为可以转换为 FooWrapper
的事物添加一个实现,但这不会起作用,因为这些实现可能会重叠(并且 specialization 还不稳定)。
但是您可以为 FooImpl
:
IntoBaz
实现
impl IntoBaz for FooImpl {
fn into(self) -> Baz {
IntoBaz::into(FooWrapper::from(self))
}
}
这将使您的代码编译:
fn main() {
do_thing(&BarImpl);
do_thing(&FooImpl);
}
From
和 Into
在类型级别上没有任何特殊意义。
但是,您可能只是问得太狭隘了。看起来您希望 do_thing(&BarImpl)
和 do_thing(&FooImpl)
编译并执行 "right" 操作。如果这就是您所需要的,那么有一个有点棘手的替代方法可以工作:向 IntoBaz
添加一个类型参数并使用不同的类型使 impl
不重叠。
trait IntoBaz<T> {
fn into_baz(self) -> Baz;
}
struct ByFoo;
impl<F: Foo> IntoBaz<ByFoo> for F {
fn into_baz(self) -> Baz {
Baz
}
}
struct ByBar;
impl<B: Bar> IntoBaz<ByBar> for B {
fn into_baz(self) -> Baz {
Baz
}
}
do_thing
现在可以通用于 T
:
fn do_thing<T, B: IntoBaz<T>>(_: &B) {}
调用时,如果只有一个T
有效,编译器会自动找到
fn main() {
do_thing(&BarImpl);
do_thing(&FooImpl);
}
I'm trying to add
io::Write
support to a library withfmt::Write
support without introducing a breaking change.
不幸的是,这个建议在技术上是一个重大变化。如果有某种类型同时实现了 io::Write
和 fmt::Write
,那么 do_thing(&implements_both)
(过去使用 fmt::Write
)现在将失败由于歧义而编译。但是任何 trait 选择明确的地方仍然会像以前一样编译,所以破损的风险要低得多。
另请参阅:
- Can I avoid eager ambiguity resolution for trait implementations with generics?