构建命令行程序
获取命令行参数的值,我们需要一个 Rust 标准库提供的函数,也就是 std::env::args。
当所需函数嵌套了多于一层模块时,通常将父模块引入作用域,而不是其自身。
std::fs
来处理文件
获取环境变量的方法,env::var(环境变量名)
标准库提供了 eprintln!
宏来打印到标准错误流
闭包
闭包的定义以一对竖线(|
)开始,在竖线中指定闭包的参数,如果有多于一个参数,可以使用逗号分隔,比如 |param1, param2|
如果尝试对同一闭包使用不同类型则会得到类型错误。
Fn
系列 trait 由标准库提供。所有的闭包都实现了 trait Fn
、FnMut
或 FnOnce
中的一个。
闭包可以通过三种方式捕获其环境,他们直接对应函数的三种获取参数的方式:获取所有权,可变借用和不可变借用。这三种捕获值的方式被编码为如下三个 Fn
trait:
FnOnce
消费从周围作用域捕获的变量,闭包周围的作用域被称为其 环境,environment。为了消费捕获到的变量,闭包必须获取其所有权并在定义闭包时将其移动进闭包。其名称的Once
部分代表了闭包不能多次获取相同变量的所有权的事实,所以它只能被调用一次。FnMut
获取可变的借用值所以可以改变其环境Fn
从其环境获取不可变的借用值
如果你希望强制闭包获取其使用的环境值的所有权,可以在参数列表前使用 move
关键字。这个技巧在将闭包传递给新线程以便将数据移动到新线程中时最为实用。
迭代器
迭代器(iterator)负责遍历序列中的每一项和决定序列何时结束的逻辑。
在 Rust 中,迭代器是惰性的(lazy),这意味着在调用方法使用迭代器之前它都不会有效果。
迭代器都实现了一个叫做 Iterator
的定义于标准库的 trait。
cargo
当项目的 Cargo.toml 文件中没有任何 [profile.*]
部分的时候,Cargo 会对每一个配置都采用默认设置。通过增加任何希望定制的配置对应的 [profile.*]
部分,我们可以选择覆盖任意默认设置的子集。
将crate发布到crates.io
文档注释使用三斜杠 ///
而不是两斜杆并支持 Markdown 注解来格式化文本。文档注释就位于需要文档的项的之前。
还有另一种风格的文档注释,//!
,这为包含注释的项,而不是注释之后的项增加文档。这通常用于 crate 根文件(通常是 src/lib.rs)或模块的根文件为 crate 或模块整体提供文档。
pub use
重导出(re-export)项来使公有结构不同于私有结构。
https://crates.io/me/ 的账户设置页面并获取 API token。接着使用该 API token 运行 cargo login
命令
创建了一个账号,保存了 API token,为 crate 选择了一个名字,并指定了所需的元数据,cargo publish 发布
撤回某个版本,cargo yank --vers 版本号
本地安装和使用二进制crate,cargo install crate名
智能指针
智能指针(smart pointers)是一类数据结构,他们的表现类似指针,但是也拥有额外的元数据和功能。
智能指针区别于常规结构体的显著特性在于其实现了 Deref
和 Drop
trait。Deref
trait 允许智能指针结构体实例表现的像引用一样,这样就可以编写既用于引用又用于智能指针的代码。
常见的智能指针类型:
Box<T>
,用于在堆上分配值Rc<T>
,一个引用计数类型,其数据可以有多个所有者Ref<T>
和RefMut<T>
,通过RefCell<T>
访问,一个在运行时而不是在编译时执行借用规则的类型。
Box多用于以下场景:
- 当有一个在编译时未知大小的类型,而又想要在需要确切大小的上下文中使用这个类型值的时候
- 当有大量数据并希望在确保数据不被拷贝的情况下转移所有权的时候
- 当希望拥有一个值并只关心它的类型是否实现了特定 trait 而不是其具体类型的时候
实现 Deref
trait 允许我们重载 解引用运算符,实现 Deref
trait 的智能指针可以被当作常规引用来对待,可以编写操作引用的代码并用于智能指针。
解引用强制多态(deref coercions)是 Rust 表现在函数或方法传参上的一种便利。其将实现了 Deref
的类型的引用转换为原始类型通过 Deref
所能够转换的类型的引用。eg: & Box<String> 等同于 &String 等同于 &Str
类似于如何使用 Deref
trait 重载不可变引用的 *
运算符,Rust 提供了 DerefMut
trait 用于重载可变引用的 *
运算符。
Rust 在发现类型和 trait 实现满足三种情况时会进行解引用强制多态:
- 当
T: Deref<Target=U>
时从&T
到&U
。 - 当
T: DerefMut<Target=U>
时从&mut T
到&mut U
。 - 当
T: Deref<Target=U>
时从&mut T
到&U
。
头两个情况除了可变性之外是相同的:第一种情况表明如果有一个 &T
,而 T
实现了返回 U
类型的 Deref
,则可以直接得到 &U
。第二种情况表明对于可变引用也有着相同的行为。
第三个情况有些微妙:Rust 也会将可变引用强转为不可变引用。但是反之是 不可能 的:不可变引用永远也不能强转为可变引用。因为根据借用规则,如果有一个可变引用,其必须是这些数据的唯一引用(否则程序将无法编译)。
Rust 并不允许我们主动调用 Drop
trait 的 drop
方法;当我们希望在作用域结束之前就强制释放变量的话,我们应该使用的是由标准库提供的 std::mem::drop
。
Rc<T>
用于当我们希望在堆上分配一些内存供程序的多个部分读取,而且无法在编译时确定程序的那一部分会最后结束使用它的时候。如果确实知道哪部分是最后一个结束使用的话,就可以令其成为数据的所有者同时正常的所有权规则就可以在编译时生效。
克隆Rc<T> 会增加引用计数
如下为选择 Box<T>
,Rc<T>
或 RefCell<T>
的理由:
Rc<T>
允许相同数据有多个所有者;Box<T>
和RefCell<T>
有单一所有者。Box<T>
允许在编译时执行不可变或可变借用检查;Rc<T>
仅允许在编译时执行不可变借用检查;RefCell<T>
允许在运行时执行不可变或可变借用检查。- 因为
RefCell<T>
允许在运行时执行可变借用检查,所以我们可以在即便RefCell<T>
自身是不可变的情况下修改其内部的值。
也可以通过调用 Rc::downgrade
并传递 Rc
实例的引用来创建其值的 弱引用(weak reference)。调用 Rc::downgrade
时会得到 Weak<T>
类型的智能指针。不同于将 Rc<T>
实例的 strong_count
加一,调用 Rc::downgrade
会将 weak_count
加一。Rc<T>
类型使用 weak_count
来记录其存在多少个 Weak<T>
引用,类似于 strong_count
。其区别在于 weak_count
无需计数为 0 就能使 Rc
实例被清理。