Rust能力养成系列之(25): 标准库特性(上)

前言

Rust的标准库有很多内置特性,许多非常神奇的语法都与trait有关。这些特性还提供了一个很好的基线,以此,相关crate的开发者可以为他们的库提供一个通畅的接口。在本节中,我们将探索标准库特性的一些抽象考量和便利之处,这些都统统增强了crate作者开发和用户使用的体验。

 

复数类型 上

这里,我们将从crate作者的角度进行探索,并创建一个支持复数(complex number)类型的库。

这个例子很好地介绍了im的共同特点。如果你要创建自己的create,这个例子可以很好的介绍在开发过程中所须实现的共同特性。

 

我们首先通过运行cargo new complex——lib来创建一个新项目。开始,我们需要将复数表示为一个类型,这里会使用一个结构体,因为复数结构体有两个字段:复数的实部和虚部。下面是我们对它的定义:

// complex/src/lib.rs
 
struct Complex<T> {
    // Real part
    re: T,
    // Complex part
    im: 

可见T在这里是对泛型的声明,因为re和im都可以是浮点数或整数值。为了让这种类型达到预期效果,我们需要创建其实例方法。通常是使用实现关联的方法new,在这里需要传递re和im的值。如果我们还想用默认值初始化一个复数值(比如re = 0, im = 0),该怎么办?为此,可以有一个名为Default的特性。对于用户定义的类型来说,实现Default非常简单;我们可以在复数结构上放置一个#[derive(Default)]属性来自动实现它的默认特性。需要多提一点,Default只能为结构体、枚举或集合体(structs, enums, or unions)的成员和字段本身提供实现。

现在我们更新一下上面的代码,可以看到new和Default都在其中:

// complex/src/lib.rs
 
#[derive(Default)]
struct Complex<T> {
    // Real part
    re: T,
    // Complex part
    im: T
}
 
impl<T> Complex<T> {
    fn new(re: T, im: T) -> Self {
        Complex { re, im }
    }
}
 
#[cfg(test)]
mod tests {
    use Complex;
    #[test]
    fn complex_basics() {
        let first = Complex::new(3,5);
        let second: Complex<i32> = Complex::default();
        assert_eq!(first.re, 3);
        assert_eq!(first.im, 5);
        assert!(second.re == second.im);
    }
}

 

我们还在tests模块的底部添加了一个简单的初始化测试用例。#[derive(默认)]属性功能是作为一个过程宏(procedural macro)实现的,它可以自动实现出现在其上的类型特性。这种自动派生(auto-deriving)要求任何自定义类型的字段(如结构或枚举)也实现Default 特性本身。如上所说,使用这种派生特性只适用于结构、枚举和联合。我们将在第9章涉及用宏进行元语言编程中,来研究如何编写我们自己的派生过程宏。此外,函数new实际上并不是一个特殊的构造函数(如果您熟悉使用构造函数的语言),而是开源社区采取的一个约定俗成的结果,已被大家接受作为创建类型的新实例的方法名称。

现在,在进入更复杂的特性实现之前,我们需要自动派生一些更多的内置特性,这些特征将帮助我们实现更高级的功能,以下是一些重要的内容:

  • Debug:这个之前我们之前已经见过了。顾名思义,此特性有助于在控制台上打印用于调试的类型。对于复合类型(composite type),类型将以带有大括号和圆括号的类似json(JSON-like)的格式打印,如果类型是字符串,则使用引号。在Rust中,这是为大多数内置类型所实现的。
  • PartialEq 和Eq:这两个特性使得两个量可以进行平等的比较。对于我们的复数类型,只有PartialEq是有意义的,因为当复数类型包含f32或f64值时,不能进行比较,因为Eq没有为f32和f64值实现相关方法。PartialEq定义了部分排序(partial ordering),而Eq需要一个总体排序(total ordering),但是对于浮点数的总体排序没有进行定义,因为NaN不等于NaN。其中,NaN是浮点类型中的类型,表示其结果未定义的操作,如0.0 / 0.0。
  • Copy 和Clone:这两个特性定义了类型如何被复制。我们会在第6章涉及内存管理和安全中,有单独的一节介绍它们。简而言之,当在任何自定义类型上自动派生时,Copy和Clone可以保证从实例创建一个新的副本,或者在实现copy时隐式创建,或者在实现clone时显式调用clone()时创建。请注意,Copy特性依赖于在类型上实现的Clone。

 

做完这些解释之后,我们将为这些内置特性添加auto- derived,如下所示:

#[derive(Default, Debug, PartialEq, Copy, Clone)]
struct Complex<T> {
    // Real part
    re: T,
    // Complex part
    im: T
}

接下来,让我们为复数<T>类型添砖加瓦,以便有更好的人体工程学性质和用户体验。我们将实现一些额外特性(排名不分先后)如下:

  • 来自std::ops模块的Add ,它可以让我们使用+运算符来进行复数加法
  • 来自std::convert模块的Into和From,以此能够从其他类型创建复数
  • Display,打印复数类型数据,提升可读性

让我们从Add特性的实现开始。该特性的具体文档在https://doc.rust-lang.org/std/ops/trait.Add.html上相关声明如下:

pub trait Add<RHS = Self> { 
    type Output; 
    fn add(self, rhs: RHS) -> Self::Output; 
} 

这里对该代码部分解释一下:

  • pub trait Add<RHS = Self>说明Add是一个具有泛型类型RHS的trait,该泛型类型默认设置为Self。在这里,Self是实现该特性的类型别名,在我们的例子里是指Complex。应该说,在trait中引用代码实现主体或执行器(implementer)是一种很方便的方式。
  • Output是执行器需要声明的一种连带类型
  • fn add(self, rhs: RHS) -> Self::Output是Add特性提供的核心功能,该方法在两个实现类型之间使用+操作符时被调用。这是一个实例方法,以self为值并以rhs作为参数,也就是特性定义中的RHS。在我们的例子中,+操作符的左边和右边的默认类型是相同的,但是当我们编写impl块时,RHS可以更改为任何其他类型。例如,我们可以有一个添加Meter(米)和Centimeter厘米类型的实现,在这种情况下,我们会在impl块中写入RHS= Centimeter。代码的 最后,是说add方法必须返回我们在第2行用Self::Output语法声明的输出类型。

 

结语

到这里准备动作基本到位了,下一篇,我们来跑一下相关的代码。

 

主要参考和建议读者进一步阅读的文献

https://doc.rust-lang.org/book

Rust编程之道,2019, 张汉东

The Complete Rust Programming Reference Guide,2019, Rahul Sharma,Vesa Kaihlavirta,Claus Matzinger

Hands-On Data Structures and Algorithms with Rust,2018,Claus Matzinger

Beginning Rust ,2018,Carlo Milanesi

Rust Cookbook,2017,Vigneshwer Dhinakaran

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值