根据特征的通用实现 [英] Generic implementation depending on traits

查看:41
本文介绍了根据特征的通用实现的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在定义泛型 struct 时, Rust 中有一种方法可以根据给定的泛型类型实现的特征使用方法的不同实现T ?

When defining a generic struct, is there a way in Rust to use different implementation of a method according to which trait is implemented by the given generic type 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();
}

推荐答案

有一个不稳定的功能,称为

There is an unstable feature known as specialization which permits multiple impl blocks to apply to the same type as long as one of the blocks is more specific than the other:

#![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具有另一种能够执行此操作的语言功能:方法解析自动引用.编译器的规则是,如果可以在没有autoref的情况下分派方法,则它将被分配.只有在没有autoref的情况下无法分派方法的情况下,编译器才会插入autoref并尝试再次解析它.因此,在此示例中:

On stable Rust, we'll need some other mechanism to accomplish specialization. Rust has another language feature capable of doing this: method resolution autoref. The compiler's rule is that if a method can be dispatched without autoref then it will be. Only if a method cannot be dispatched without autoref, the compiler will insert an autoref and attempt to resolve it again. So in this example:

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中的专业化:

The implementation for Value will be prioritized over &Value. Knowing this rule, we can mimic specialization in stable 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 的更通用的实现.

The compiler will try to use the Print implementation first. If it can't (because the type is not Display), it will then use the more general implementation of DefaultPrint.

此技术应用方法解析的方式无法用特征限制来描述,因此不适用于常规方法,因为我们必须在特征之一( DefaultPrint 打印):

The way that this technique applies method resolution cannot be described by a trait bound, so it will not work for regular methods, as we would have to choose between one of the traits (DefaultPrint or Print):

fn print<T: ???>(value: T) {
    (&value).print()
}

但是,此技巧对不需要说明特征界限的宏非常有用:

However, this trick can be very useful to macros, which do not need to spell out trait bounds:

macro_rules! print {
    ($e:expr) => {
        (&$e).print()
    };
}


print!(not_printable); // => I cannot be printed
print!(printable); // => Hello World

这篇关于根据特征的通用实现的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆