Rust泛型

我发现Rust的泛型的一个特性,就是它可以约束参数的类型,比Java更加严格。
可以提现在下面这些方面里面:

  1. 方法调用的参数上
pub trait Summary {
    fn summarize_author(&self) -> String;

    fn summarize(&self) -> String {
        format!("(Read more from {}...)", self.summarize_author())
    }
}

pub struct Post {
    pub title: String, // 标题
    pub author: String, // 作者
    pub content: String, // 内容
}

impl Summary for Post {
    fn summarize_author(&self) -> String {
        format!("@{}", self.title)
    }
}

pub struct Weibo {
    pub username: String,
    pub content: String
}

impl Summary for Weibo {
    fn summarize_author(&self) -> String {
        format!("@{}", self.username)
    }
}

pub fn notify(item1: &impl Summary, item2: &impl Summary) {
    println!("notify");
}
pub fn notify2<T: Summary>(item1: &T, item2: &T) {
    println!("notify2");
}


fn main(){
    let post = Post{title: "Rust语言简介".to_string(),author: "Sunface".to_string(), content: "Rust棒极了!".to_string()};
    let weibo = Weibo{username: "sunface".to_string(),content: "好像微博没Tweet好用".to_string()};

    notify(&post, &weibo);
    // 下面这句代码会报错
    notify2(&post,&weibo);
}

这里面notify2这句代码会报错。

public class Main {
    public static <T> void notify(T item1, T item2) {
        System.out.println(item1.toString() + " and " + item2.toString());
    }

    public static void main(String[] args) {
        notify("Hello", "World"); // OK
        notify(1, 2);             // OK
        notify("Hello", 2);    // ok  , 为什么能正常运行呢?
    }
}
  1. 集合以及方法参数上
    如果有这么一个需求,我需要用一个集合存放一些实现了某一个特征的类型。但是这些类型不是相同的类型,只不过他们都实现了相同的特征罢了。
    那么就可以用到特征对象,这个概念。
    特征对象本质上还是指针,所以它有固定的大小,所以能作为返回值。它的大小有两部分组成,一部分是指向具体类型的指针,另外一个部分是指向一个虚表。这个表里面存放着这个类型实现的那个特征的所对应的方法。
trait Draw {
    fn draw(&self) -> String;
}

impl Draw for u8 {
    fn draw(&self) -> String {
        format!("u8: {}", *self)
    }
}

impl Draw for f64 {
    fn draw(&self) -> String {
        format!("f64: {}", *self)
    }
}

// 若 T 实现了 Draw 特征, 则调用该函数时传入的 Box<T> 可以被隐式转换成函数参数签名中的 Box<dyn Draw>
fn draw1(x: Box<dyn Draw>) {
    // 由于实现了 Deref 特征,Box 智能指针会自动解引用为它所包裹的值,然后调用该值对应的类型上定义的 `draw` 方法
    x.draw();
}

fn draw2(x: &dyn Draw) {
    x.draw();
}

fn main() {
    let x = 1.1f64;
    // do_something(&x);
    let y = 8u8;

    // x 和 y 的类型 T 都实现了 `Draw` 特征,因为 Box<T> 可以在函数调用时隐式地被转换为特征对象 Box<dyn Draw> 
    // 基于 x 的值创建一个 Box<f64> 类型的智能指针,指针指向的数据被放置在了堆上
    draw1(Box::new(x));
    // 基于 y 的值创建一个 Box<u8> 类型的智能指针
    draw1(Box::new(y));
    draw2(&x);
    draw2(&y);
}

上面代码,有几个非常重要的点:

draw1 函数的参数是 Box 形式的特征对象,该特征对象是通过 Box::new(x) 的方式创建的
draw2 函数的参数是 &dyn Draw 形式的特征对象,该特征对象是通过 &x 的方式创建的
dyn 关键字只用在特征对象的类型声明上,在创建时无需使用 dyn

那如果要使用泛型来实现呢?

pub struct Screen<T: Draw> {
    pub components: Vec<T>,
}

impl<T> Screen<T>
    where T: Draw {
    pub fn run(&self) {
        for component in self.components.iter() {
            component.draw();
        }
    }
}

泛型的特点是类型必须是相同的。所以这就是一个限制了。不太灵活。但是非常清晰。
上面的 Screen 的列表中,存储了类型为 T 的元素,然后在 Screen 中使用特征约束让 T 实现了 Draw 特征,进而可以调用 draw 方法。

但是这种写法限制了 Screen 实例的 Vec 中的每个元素必须是 Button 类型或者全是 SelectBox 类型。如果只需要同质(相同类型)集合,更倾向于采用泛型+特征约束这种写法,因其实现更清晰,且性能更好(特征对象,需要在运行时从 vtable 动态查找需要调用的方法)。

特征对象的一些特点

不是所有的特征都可以作为特征对象的。必须要加上如下的两点要求:

  1. 方法的返回类型不能是 Self
  2. 方法没有任何泛型参数

标准库中的 Clone 特征就不符合对象安全的要求:

pub trait Clone {
    fn clone(&self) -> Self;
}

对象安全对于特征对象是必须的,因为一旦有了特征对象,就不再需要知道实现该特征的具体类型是什么了。如果特征方法返回了具体的 Self 类型,但是特征对象忘记了其真正的类型,那这个 Self 就非常尴尬,因为没人知道它是谁了。但是对于泛型类型参数来说,当使用特征时其会放入具体的类型参数:此具体类型变成了实现该特征的类型的一部分。而当使用特征对象时其具体类型被抹去了,故而无从得知放入泛型参数类型到底是什么。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值