nya*_*108 9 generics traits rust
当定义泛型时, Ruststruct有没有办法根据给定泛型类型实现的特征来使用方法的不同实现?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();
}
Run Code Online (Sandbox Code Playgroud)
Ibr*_*med 12
有一个不稳定的功能,只要其中一个块比另一个块更具体specialization,它就允许多个impl
块应用于同一类型:
#![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
Run Code Online (Sandbox Code Playgroud)
在稳定的 Rust 上,我们需要一些其他机制来完成专业化。Rust 还有另一个语言功能可以做到这一点:方法解析 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");
}
}
Run Code Online (Sandbox Code Playgroud)
的实施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
Run Code Online (Sandbox Code Playgroud)
编译器将首先尝试使用该Print实现。如果不能(因为类型不是Display),它将使用更通用的实现DefaultPrint。
该技术应用方法解析的方式无法通过特征界限来描述,因此它不适用于常规方法,因为我们必须在特征 (DefaultPrint或Print) 之一之间进行选择:
fn print<T: ???>(value: T) {
(&value).print()
}
Run Code Online (Sandbox Code Playgroud)
然而,这个技巧对于宏来说非常有用,因为它不需要阐明特征边界:
macro_rules! print {
($e:expr) => {
(&$e).print()
};
}
print!(not_printable); // => I cannot be printed
print!(printable); // => Hello World
Run Code Online (Sandbox Code Playgroud)