我写了一个类型Wrapper<T>
,其中包含T
的值:
struct Wrapper<T>(T);
我想要一个方法to_wrap
,这将允许我编写这样的代码,其中b
是Wrapper<i32>
和c
是Wrapper<i32>
:
let a = 12i32;
let b = a.to_wrap();
let c = b.to_wrap();
我希望v.to_wrap()
总能产生一个Wrapper<T>
,其中T不是Wrapper
。如果v
是Wrapper<T>
,v.to_wrap()
也将是具有相同价值的Wrapper<T>
。
我写的最接近我的想法的代码是:
#![feature(specialization)]
#[derive(Debug)]
struct Wrapper<T>(T);
trait ToWrapper<W> {
fn to_wrap(self) -> W;
}
impl<T> ToWrapper<Wrapper<T>> for T {
default fn to_wrap(self) -> Wrapper<T> {
Wrapper(self)
}
}
impl<T> ToWrapper<Wrapper<T>> for Wrapper<T> {
fn to_wrap(self) -> Self {
self
}
}
fn main() {
let a = 1i32;
println!("{:?}", a);
let a = 1.to_wrap();
println!("{:?}", a);
let a: Wrapper<i32> = 1.to_wrap().to_wrap();
// let a = 1.to_wrap().to_wrap();
// boom with `cannot infer type`
println!("{:?}", a);
}
如果我从let a: Wrapper<i32> = 1.to_wrap().to_wrap()
中删除类型注释,则会出现编译错误:
error[E0282]: type annotations needed
--> src/main.rs:27:9
|
27 | let a = 1.to_wrap().to_wrap();
| ^
| |
| cannot infer type
| consider giving `a` a type
我希望Rust编译器自动派生出1.to_wrap().to_wrap()
的类型。如何编写正确的版本,或者为什么不能编写它?
我不相信你现在可以通过专业化来实现你实现单一特质的目标。
您的特征定义允许相同类型的特征的多个实现:
trait ToWrapper<W> {
fn to_wrap(self) -> W;
}
impl ToWrapper<i32> for u8 {
fn to_wrap(self) -> i32 {
i32::from(self)
}
}
impl ToWrapper<i16> for u8 {
fn to_wrap(self) -> i16 {
i16::from(self)
}
}
有了这样的设置,就不可能知道to_wrap
应该是什么样的结果;你总是需要以某种方式提供输出类型。然后通过尝试在未知类型上调用to_wrap
来产生另一种未知类型来复合问题!
通常,使用关联类型将是解决方案,但由于它们与专业化的交互方式,您无法切换到此处。
也可以看看:
你可以实现这样的事情,不是使用专业化,而是使用两种不同的特征和一个自动特征来区分它们。
#![feature(optin_builtin_traits)]
auto trait IsWrap {}
#[derive(Debug)]
struct Wrapper<T>(T);
impl<T> !IsWrap for Wrapper<T> {}
trait ToWrapper: Sized {
fn to_wrap(self) -> Wrapper<Self>;
}
impl<T: IsWrap> ToWrapper for T {
fn to_wrap(self) -> Wrapper<T> {
Wrapper(self)
}
}
trait ToWrapperSelf {
fn to_wrap(self) -> Self;
}
impl<T> ToWrapperSelf for Wrapper<T> {
fn to_wrap(self) -> Self {
self
}
}
fn main() {
let a = 1.to_wrap();
println!("{:?}", a);
let a = 1.to_wrap().to_wrap();
println!("{:?}", a);
}
与chabapok's suggestion一样,你不能使用这种技术来编写一个泛型函数,当给定一种类型时,它会以一种方式运行,而另一种类型则使用另一种方式(尽管请参见下面的链接)。但是,当编译器知道具体类型而不是程序员知道具体类型时,可以使用它 - 宏可以用作一个可能的用例。
它还有一个优点,就是在任何类型上调用to_wrap
方法都不存在歧义。每种类型最多只有一种to_wrap
方法,因为Wrapper
s只有ToWrapperSelf::is_wrap
而非Wrapper
s只有ToWrapper::is_wrap
。
另一个缺点是!IsWrap
的Wrapper<T>
是“传染性的”:包含或可能包含Wrapper<T>
的任何类型也将自动!IsWrap
。如果在这种类型上调用.to_wrap()
,编译器将无法找到该方法并将发出错误。如果这是一个问题,您可以为这些类型手动实现IsWrap
,但是寻找另一个不那么脆弱的解决方案可能更为谨慎。
(上面唯一的例外是Box<Wrapper<T>>
:你可以在它上面调用ToWrapperSelf::to_wrap
来获得Wrapper<T>
。这是因为自动解引用规则和Box
is special。)
Wrapper
s和非Wrapper
s,即使你不能充分利用专业化的全部功能。#[derive(Debug)]
struct Wrapper<T>(T);
trait ToWrapper<W> {
fn to_wrap(self) -> Wrapper<W>;
}
impl<T> ToWrapper<T> for T {
fn to_wrap(self) -> Wrapper<T> {
Wrapper(self)
}
}
impl<T> Wrapper<T> {
fn to_wrap(self) -> Wrapper<T> {
self
}
}
fn main() {
let a = 1i32;
println!("{:?}", a);
let a = 1.to_wrap();
println!("{:?}", a);
let a: Wrapper<i32> = 1.to_wrap().to_wrap();
let b = 1.to_wrap().to_wrap();
println!("{:?}", a);
println!("{:?}", b);
}