写点什么

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

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

    阅读完需:约 4 分钟

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

到目前为止,我们已经学习了 Rust 中大部分基础支持,而且在实践课中我们也跟着老师一起构建了一个 kv server 系统。从某种程度上讲,我们也算是一个入门的 Rustaceans 了。从今天开始,我们开始跟着老师学习进阶知识了。今天的主题是如何在实战中使用泛型编程。

01-泛型数据结构的逐步约束

在数据结构的定义中使用泛型我们已经见过很多,例如 Box<T>,Option<T> 等。在定义时,泛型对待处理的类型做了一个抽象,也就是说,具体什么类型并不关心。到了实现阶段,根据具体的需求不同,可以对泛型中的抽象类型进行具体的限制。例如:


// 为所有的 Option<T> 实现 xxximpl<T> Option<T> { .. }// 为实现了 fmt::Debug 类型的 Option<T> 实现 Debug traitimpl<T> fmt::Debug for Option<T> where T: fmt::Debug {...}
复制代码


上述就展示了对泛型数据结构的逐步约束,从没有任何约束到约束为实现了 Debug trait 的类型。


通过使用泛型参数,数据结构的定义者将决策交给了数据结构的使用者。这符合《代码整洁之道》中的观点:“架构师的工作不是作出决策,而是尽可能久地推迟决策,在现在不作出重大决策的情况下构建程序,以便以后有足够信息时再作出决策”。决策的推迟也使得系统架构更加的灵活,面对未来的变更也就更从容。

02-泛型参数的常用场景

02.1-使用泛型参数延迟类型绑定

泛型参数最为普遍的用法便是用来延迟数据类型的绑定。回想之前实践课程中定义的 Service 数据结构:


pub struct Service<Store = MemTable> {    inner: Arc<ServiceInner<Store>>,}
复制代码


定义时不关心泛型的具体类型,仅到实现时才知道。这种用法太普遍了,也比较直观,容易理解。

02.2-泛型参数与幽灵数据

Rust 中的幽灵数据(PhantomData)有点类似于 Java 中的幽灵类型(Phantom Type)。第一次接触幽灵数据是在学习 Box<T>时,其内部实现的 Unique<T> 的定义中就用到了:


pub struct Unique<T: ?Sized> {    pointer: NonNull<T>,    // NOTE: this marker has no consequences for variance, but is necessary    // for dropck to understand that we logically own a `T`.    //    // For details, see:    // https://github.com/rust-lang/rfcs/blob/master/text/0769-sound-generic-drop.md#phantom-data    _marker: PhantomData<T>,}
复制代码


幽灵数据本身并不占用内存,即其大小为 0,主要是用来通知编译器在数据结构的定义中未使用到,但在实现过程中需要使用的参数。如果没有幽灵数据,编译器在编译时会自动删除未使用的泛型参数。

02.3-泛型参数与 trait 的多个实现

泛型参数的另外一个常用用途就是实现泛型数据结构对同一个 trait 的不同实现。例如:


pub struct Linear;pub struct Quadratic;
pub struct Equation<T> {...};
impl Iterator for Equation<Linear> {...}impl Iterator for Equation<Quadratic> {...}
复制代码


我们知道,泛型在编译期间会单态化,Equation<Linear> 和 Equation<Quadratic> 可以看作是不同的数据类型。


这样做的好处是:用泛型数据结构来统一相同的逻辑,用泛型参数的具体类型来处理变化的逻辑。


本节课程链接《23|类型系统:如何在实战中使用泛型编程?

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

Samson

关注

还未添加个人签名 2019.07.22 加入

还未添加个人简介

评论

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