Rust学习笔记 常用trait 类型转换,操作符相关
前两天我们学习了内存相关,标记trait,今天我们来学习一下类型转换和操作符相关的常用trait。
在开发中,我们经常需要把一个类型转换成另一种类型。
我们先来看下,这几种方式的比较。
(资料图)
这还用比吗?显然第二种,对于我们这种码农来说更友好,只需要记一种格式就行了。不同类型的转换都实现一个数据转换trait,这样可以用同一个方法实现不同类型的转换,(有点像泛型?)这样也符号开闭原则,对扩展开放,对修改关闭。底层可以扩展更多的数据类型,原来的不用修改,只需要新增实现即可。
按照这个思路,Rust按照值类型和引用类型提供两套不同的trait。
值类型: From <T> / Into <T> / TryFrom <T> / TryInto <T>
引用类型: AsRef <T> / AsMut <T>
值类型
先看值类型的 From和Into的代码定义:
从代码中可以看到,在实现From的时候会自动实现Into。
一般情况,只用实现From,这2种方式都可以做类型做转换。 比如这样:
我们再来看一下 Into 是怎么让代码变的灵活的吧。
注意:如果你的数据类型在转换过程中有可能出现错误,可以使用 TryFrom <T> 和 TryInto <T>,它们的用法和 From / Into 一样,只是 trait 内多了一个关联类型 Error,且返回的结果是 Result<T, Self::Error>。
引用类型
AsRef<T> 和 AsMut<T> 用于从引用到引用的转换。还是先看它们的代码定义:
从这2个的定义可以看出,允许T的大小可变类型,如:str、[u8]之类的。 另外AsMut除了是可变引用之外,其他的都和AsRef一样,所以我们主要看AsRef。
我们还是通过一坨代码来感受一下怎么用AsRef。
对于已经实现了AsRef的 &str和String我们可以直接使用,对于还没有实现的Language,我们需要手动实现一下。
现在我们队如何使用 From / Into / AsRef / AsMut 进行类型间转换,有了初步的了解。
操作符相关
我们之前学习过Addtrait,它可以重载加法运算符。 其实Rust为所有的运算符都提供了trait,你可以为自己的类型重载一些操作符。
Deref 和 DerefMut
今天重点要介绍的操作符是 Deref 和 DerefMut。 还是先看下 代码定义:
可以看到DerefMut "继承"了Deref,还多了一个方法deref_mut,用来获取可变的解引用。
对于普通的引用,解引用很直观,因为它只有一个指向值的地址。 但是对于只能指针来说,解引用就没那么直观了. 我们来看Rc是如何实现Deref的
可以看到,它最终指向了堆上的 RcBox 内部的 value 的地址,然后如果对其解引用的话,得到了 value 对应的值。以下图为例,最终打印出 v = 1。
在 Rust 里,绝大多数智能指针都实现了 Deref,我们也可以为自己的数据结构实现 Deref。
还是用一坨代码来感受一下
我们为Buffer实现了Deref和DerefMut,这样在解引用的时候,直接访问到buf.0。
这里有一个比较有意思的点:
我们并没有对Buff实现sort方法。 main里的buf.sort()也没有做解引用的操作,但是却相当于直接访问了buf.0.sort()。
这是因为sort()方法的第一个参数是&mut self, 这里的buf.sort() 相当于 Vec::sort(&mut buf)。
此时 Rust 编译器会强制做 Deref/DerefMut 的解引用,所以这相当于 (*(&mut buf)).sort()。
不过,我刚开始学,其实还没有太明白!这里的弯弯绕。
其他
Debug / Display
代码定义如下:
有点奇怪的是 这两的定义是一样的。Debug是我们码农调试打印用的,Display是给用户展示用的。在使用的时候,Debug 用 {:?} 来打印,Display 用 {} 打印。
Default trait
为数据类型提供默认值,定义如下:
可以用#[derive(Default)]来生成实现,还是和之前的要求一样,组合类型里的每个字段都需要实现Default trait才可以。
举个栗子:
小结
这几天我们一起学习了内存、类型转换,操作符等常用的trait。
一个设计良好的 trait 可以大大提升整个系统的可用性和扩展性。
明天我们继续学习智能指针。
如果你觉得有点收获,欢迎点个关注, 也欢迎分享给你身边的朋友。