我写了一个类型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 NOT 不是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()
的类型。如何编写正确的版本,或者为什么无法编写?
答案 0 :(得分:2)
我认为您目前无法通过专业化来实现实现单个特征的目标。
您的特征定义允许同一类型的特征的多种实现:
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
,使问题更加复杂!
通常,使用关联的类型将是解决方案,但是由于它们与专业化的交互方式,您不能在此切换到那些类型。
另请参阅:
答案 1 :(得分:2)
您可以实现这样的目标,而不是使用专业化,而是使用两个不同的特征和一个自动特征来区分它们。
#![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
仅具有ToWrapperSelf::is_wrap
,非Wrapper
仅具有ToWrapper::is_wrap
。
另一个 dis 的优点是!IsWrap
的{{1}}是“传染性的”:任何包含或可能包含Wrapper<T>
的类型也会 自动为Wrapper<T>
。如果您在这种类型上调用!IsWrap
,则编译器将无法找到该方法,并会发出错误消息。如果这是一个问题,则可以为这些类型手动实现.to_wrap()
,但是寻找另一个较不脆弱的解决方案可能更为谨慎。
(以上唯一的例外是IsWrap
:您可以在其上调用Box<Wrapper<T>>
以获得ToWrapperSelf::to_wrap
。由于自动取消引用规则和Box
is special )
Wrapper<T>
和非Wrapper
,即使您无法充分利用专业化的全部力量。 / li>
答案 2 :(得分:0)
#[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);
}