写点什么

每日一 R「18」类型系统进阶(二)

作者:Samson
  • 2022 年 8 月 28 日
    上海
  • 本文字数:1073 字

    阅读完需:约 4 分钟

每日一 R「18」类型系统进阶(二)

今天的课程主题是实战中的 trait object 使用方法。在运行时,我们只关注类型表现出某个 trait 的行为,而不关心其具体类型时就可以使用 trait object,用法时 &dyn T 或 Box<dyn T>。Trait object 是一个固定大小的胖指针,同时指向具体类型值和 vtable。


从本质上讲,trait object 和 泛型一样,是 Rust 实现多态的一种方式。Trait object 也是一种延迟绑定,可以推迟决策到运行时。Trait object 使用的是动态分配,因此在获得较高灵活性的同时,也失去了部分执行效率。

01-trait object 作为函数参数

Trait object 作为函数参数时的用法如下(基于动态分发):


/// 使用 trait object: &dyn Tpub fn execute_trait_object(cmd: &dyn Executor) -> Result, BoxedError> {     cmd.run()}/// 使用 trait object: Boxpub fn execute_boxed_trait_object(cmd: Box) -> Result, BoxedError> {     cmd.run()}
复制代码

02-trait object 作为函数返回值

Trait object 也可作为函数的返回值:


pub trait Storage: Send + Sync + 'static {    ...    /// 遍历 HashTable,返回 kv pair 的 Iterator    fn get_iter(&self, table: &str) -> Result<Box<dyn Iterator<Item = Kvpair>>, KvError>;}
复制代码


上例中返回值的类型是 Result<T, E>,其中 T 为在堆上分配的 trait object,E 为 KvError。

03-trait object 作为数据结构的域

Trait object 除了在函数中使用,还可作为数据结构的域:


pub struct HandshakeState {    pub(crate) rng:              Box<dyn Random>,    ...}
复制代码


其实上述结构也可通过泛型实现,例如:


pub struct HandshakeState<T> {    pub(crate) rng:              T,    ...}
复制代码


但是,相比于 trait object,使用泛型有两个主要的缺点:


  1. 如果泛型个数比较多,代码就会比较臃肿,可读性降低;

  2. 任何使用数据结构的地方,都必须带着泛型以及泛型参数的约束,写起来不方便。


综上,使用 trait object 虽然会损失部分执行性能(由动态分配导致),但会提高代码可读性,而且代码只有一份实现。


在数据结构中使用 trait object 还有一个典型的场景:闭包。例如:


#[derive(Clone)]pub struct AttributeGetter(    Arc<dyn Fn(&Instance, &mut Host) -> crate::Result<PolarValue> + Send + Sync>,);
复制代码


本节课程链接《24|类型系统:如何在实战中使用trait object?


历史文章推荐

每日一 R「13」数据结构(四)闭包

每日一 R「12」数据结构(三)哈希表

每日一 R「11」数据结构(二)切片

每日一 R「10」数据结构(一)智能指针

每日一 R「09」类型系统(三)

每日一 R「08」类型系统(二)

每日一 R「07」类型系统(一)

发布于: 刚刚阅读数: 3
用户头像

Samson

关注

还未添加个人签名 2019.07.22 加入

还未添加个人简介

评论

发布
暂无评论
每日一 R「18」类型系统进阶(二)_学习笔记_Samson_InfoQ写作社区