generics - 取决于特征的通用实现

标签 generics rust traits

定义泛型时 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");
    
    (&not_printable).print();
    (&printable).print();
}

// => I cannot be printed
// => Hello World
编译器将尝试使用 Print先执行。如果不能(因为类型不是 Display ),它将使用更通用的 DefaultPrint 实现.
这种技术应用方法解析的方式不能用特征绑定(bind)来描述,因此它不适用于常规方法,因为我们必须在特征之一( DefaultPrintPrint )之间进行选择:
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/

相关文章:

java - 为什么编译器不推断泛型中的捕获关系?

c# - 如何使列表项值不引用对象

pointers - 为什么打印指针与打印取消引用的指针打印相同的内容?

java - ? Java super 字符串下界

c# - 如何使用 QueryOver 返回通用列表

string - 如何在单个函数中接受&str、String 和&String?

rust - 是否允许多态变量?

rust - Rust 可以调用抽象函数吗?

php - 我应该如何以及在何处使用 php 中的关键字 "use"

rust - 如何定义关联类型的特征边界?