定义泛型时 struct
,有没有办法在 rust 根据给定的泛型类型实现特征的方法的不同实现 T
?
例如:
struct S<T> {
value: T,
}
impl<T> S<T> {
fn print_me(&self) {
println!("I cannot be printed");
}
}
impl<T: std::fmt::Display> S<T> {
fn print_me(&self) {
println!("{}", self.value);
}
}
fn main() {
let s = S { value: 2 };
s.print_me();
}
最佳答案
有一个称为 specialization
的不稳定功能。允许多个 impl
只要其中一个 block 比另一个更具体, block 即可应用于相同类型:
#![feature(specialization)]
struct Printer<T>(T);
trait Print {
fn print(&self);
}
// specialized implementation
impl<T: fmt::Display> Print for Printer<T> {
fn print(&self) {
println!("{}", self.0);
}
}
// default implementation
impl<T> Print for Printer<T> {
default fn print(&self) {
println!("I cannot be printed");
}
}
struct NotDisplay;
fn main() {
let not_printable = Printer(NotDisplay);
let printable = Printer("Hello World");
not_printable.print();
printable.print();
}
// => I cannot be printed
// => Hello World
在稳定的 Rust 上,我们需要一些其他机制来完成特化。 Rust 有另一个语言特性可以做到这一点:method resolution autoref .编译器的规则是,如果一个方法可以在没有 autoref 的情况下被调度,那么它将是。只有在没有 autoref 的情况下无法调度方法时,编译器才会插入 autoref 并尝试再次解析它。所以在这个例子中:impl Print for Value {
fn print(self) {
println!("Called on Value");
}
}
impl Print for &Value {
fn print(self) {
println!("Called on &Value");
}
}
Value
的实现将优先于 &Value
.知道了这条规则,我们就可以模仿稳定 Rust 的特化:struct Printer<T>(T);
trait Print {
fn print(&self);
}
// specialized implementation
impl<T: fmt::Display> Print for Printer<T> {
fn print(&self) {
println!("{}", self.0);
}
}
trait DefaultPrint {
fn print(&self);
}
// default implementation
//
// Note that the Self type of this impl is &Printer<T> and so the
// method argument is actually &&T!
// That makes this impl lower priority during method
// resolution than the implementation for `Print` above.
impl<T> DefaultPrint for &Printer<T> {
fn print(&self) {
println!("I cannot be printed");
}
}
struct NotDisplay;
fn main() {
let not_printable = Printer(NotDisplay);
let printable = Printer("Hello World");
(¬_printable).print();
(&printable).print();
}
// => I cannot be printed
// => Hello World
编译器将尝试使用 Print
先执行。如果不能(因为类型不是 Display
),它将使用更通用的 DefaultPrint
实现.这种技术应用方法解析的方式不能用特征绑定(bind)来描述,因此它不适用于常规方法,因为我们必须在特征之一(
DefaultPrint
或 Print
)之间进行选择:fn print<T: ???>(value: T) {
(&value).print()
}
然而,这个技巧对宏非常有用,宏不需要拼写 trait bound:macro_rules! print {
($e:expr) => {
(&$e).print()
};
}
print!(not_printable); // => I cannot be printed
print!(printable); // => Hello World
关于generics - 取决于特征的通用实现,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65238305/