每日一 R「09」类型系统(三)
合理地定义和使用 trait,会让代码结构具有很好的扩展性,让系统变得非常灵活。今天我们将学习几个常用的 trait。
内存相关:Clone / Copy / Drop
标记 trait:Sized / Send / Sync
类型转换相关:From / Into/AsRef / AsMut
操作符相关:Deref / DerefMut
01-内存相关:Clone / Copy / Drop
01.1-Clone trait
Clone trait 是 Rust 标准库中的一个 trait,标准库中对它的描述为:
A common trait for the ability to explicitly duplicate an object.
它声明了两个关联函数:clone 和 clone_from
为某个类型实现 Clone trait 常用方式有如下两种:
#[derive(Clone)]
impl Clone for SomeStruct { … }
01.2-Copy trait
Copy trait 也是标准库中的 trait,它”继承”了 Clone trait。标准库中对它的描述为:
Types whose values can be duplicated simply by copying bits.
像 Copy 这种没有任何行为的 trait,也被称作为 Marker(标记)trait,可以用作 trait bound 来进行类型安全检查。
默认情况下,变量绑定时具有 move 语义,即所有权会发生转移。如果变量的类型实现了 Copy trait,变量绑定时便具有 copy 语义,即所有权不发生转移,但值按位复制一份(浅拷贝)。考虑如下示例,示例 1 使用 move 语义,示例 2 使用 copy 语义:
这两个例子中,唯一的区别就是变量绑定let y = x;
后,还允不允许继续访问 x。从本质上讲,复制或所有权转移都会发生内存按位拷贝,只不过 Rust 通过优化省略了所有权转移时的按位拷贝。
为某个类型实现 Copy trait 通常也有两种方式:
#[derive(Copy, Clone)]
impl Copy for SomeStruct { … } imple Clone for SomeStruct { … }
01.3-Clone vs. Copy
Clone trait 与 Copy trait 相比,不同点在于:
Copy 是隐式的(
let x = y
)、较低代价(an inexpensive bit-wise copy)的拷贝方式;Clone 是显示的(
x.clone()
或x.clone_from(&y)
)、代价可大可小(与具体情况相关);Rust 不允许对 Copy 进行重新实现(not overloadable),允许对 Clone 重新实现;
Copy “继承“了 Clone,所以实现 Copy trait 的类型也必须实现 Clone trait;
01.4-Drop trait
Drop trait 是标准库中定义的一个 trait。标准库对它的描述如下:
Custom code within the destructor.
Drop trait 中只定义了一个方法 drop:
drop 方法会在值析构时被调用。学习所有权时我们了解到,当一个变量离开其作用域后,它拥有的值会被释放。这里的释放就是通过 Drop trait 中的 drop 方法实现的。其实一个值的析构器包含两部分内容:
调用值的 Drop::drop 方法
如果值是一个组合类型的值,则递归地调用每个值的析构器
大多数情况下不需要实现 Drop trait,除非有特殊的需求,例如:
希望在数据声明周期结束时做额外的事情,例如打印日志。
需要释放自管资源的场景,编译器并不知道程序额外使用了哪些资源,也就无法自动释放它们。
Copy and Drop are exclusive.
无法为某个类型同时实现 Drop trait 和 Copy trait。原因是,编译器会隐式为实现 Copy trait 的类型的值进行拷贝,使得编译器很难预测每个值何时会被释放。
02-标记 trait:Sized / Send / Sync
学习 Copy trait 的时候提到,它也被称为标记 trait,即没有声明任何关联方法。除了 Copy trait 外,Rust 中还有其他的几个常用的标记 trait,例如 Sized / Send / Sync / Unpin。
02.1-Sized trait
Types with a constant size known at compile time.
实现了 Sized trait 的类型拥有编译时可知的固定长度。在使用泛型参数是,Rust 会默认为泛型参数加上 : Sized
类型约束。例如,如下两种写法是等效的:
如果需要 T 为可变长度的类型,可以通过?Sized
进行类型约束。
02.2-Send / Sync trait
Send: Types that can be transferred across thread boundaries.Sync: Types for which it is safe to share references between threads.
定义中的 auto 意味着编译器会在合适的场合自动为数据结构添加他们的实现。
This trait is automatically implemented when the compiler determines it’s appropriate.
定义中的 unsafe 意味着,如果开发着要自己实现这两个 trait,则需要为它们的安全性负责。
Send / Sync trait 是在多线程并发环境下使用的 trait,如果一个类型实现了 Send trait,则意味着它能够安全地从一个线程移动到另一个线程中,即所有权可以安全地在线程之间移动。如果一个类型实现了 Sync trait,则意味着引用可以在多个线程之间共享。
Send trait 与 Sync trait 的关系:如果一个类型 T 满足 Sync trait 当且仅当 &T 满足 Send trait。
03-类型转换相关:From / Into/AsRef / AsMut
03.1-From<T> / Into<T>
From<T> / Into<T> 是标准库中一对用于做 value-to-value 转换的 trait。它们的定义如下:
得益于标准库中的实现,如果一个类型实现了 From<T>,则会自动实现 Into<T>。这是由于:
如果 from 或 into 方法执行过程中可能出现错误,可以使用 TryFrom<T> / TryInto<T>,它们与 From<T> / Into<T> 一样,只不过返回结果是 Result<T, Self::Error>。从返回值上可以看出,TryFrom<T> / TryInto<T> 中包含了一个关联类型 Self::Error。
03.2-AsRef<T> / AsMut<T>
AsRef<T>: Used to do a cheap reference-to-reference conversion.AsMut<T>: Used to do a cheap mutable-to-mutable reference conversion.
04-操作符相关:Deref / DerefMut
Deref: Used for immutable dereferencing operations, like
*v
.DerefMut: Used for mutable dereferencing operations, like in*v = 1;
对于实现了 Deref trait 的结构 T 来说,*(t:T) 时会自动调用 deref 方法,相当于 *(t.deref())。Rust 中绝大多数智能指针都实现了 Deref trait,例如之前学到的 Rc<T>:
本节课程链接:《14|类型系统:有哪些必须掌握的trait?》
历史文章推荐
版权声明: 本文为 InfoQ 作者【Samson】的原创文章。
原文链接:【http://xie.infoq.cn/article/af947a766ead2ef875dce593a】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论