分类: rust
Rust 中调用 Drop 的时机
Drop
trait 在好些地方都有所提及, 但是它们的重点不太一样, 比如前文有介绍
Drop trait 的基本用法, 以及 所有权转移.
在这一节中, 我们重点介绍 Drop
trait 被调用的时机.
cargo sccache 缓存加速编译过程
默认情况下, cargo 在编译一个项目的第三方依赖时, 会缓存编译好的结果, 以便加速 之后编译速度.
但是, 对于跨工作区(workspace)的项目来说, 并不能很好地缓存这些第三方库编译后生成的 文件; 为此, mozilla 组织开发了 sccache 项目.
安装它也很简单, 分两步. 第一步在终端安装它:
$ cargo install sccache
Rust 语言受限的地方
最近半年陆续写了很多的 Rust 代码,跟别的语言相比,它有些地方还是蛮受限的,在表达 一个功能时需要各种弯弯绕,下面就列举几个方面的问题。
struct/union 不支持嵌套
类型嵌套可以将关联的数据结构放在一起,这个在C语言里是很常用的。像内核提供的系统
调用,比如 bpf()
以及 io_uring_setup()
,都有大量的使用。它们都是定义了一堆的
操作指令(operation-code, op-code),以及一个公用的 union
联合体,跟据不同的
操作指令来解析 union
为不同的结构。尤其是 bpf()
系统调用,使用一个接口就实现了
内核 BPF
虚拟机的所有操作,相当的强大和复杂。
在使用Rust为类似这样的 C
接口做绑定时,会很麻烦。需要先将嵌套的结构体以及 union
联合体拆成独立的片段,然后再依次组装到一起,这样写的代码,其可读性并不好,也更难
维护。
cargo-update:更新 rust 二进制程序
使用 cargo install
安装了一些 rust 工具到本地后,如果再想着要更新它们时,需要
手动一个个更新,比较麻烦。可以使用 cargo-update
这个工具来批量更新。
首先把它安装到系统里:
$ cargo install cargo-update
之后就可以尝试着更新本地的可执行文件了:
$ cargo install-update --all
Rust 中检查是大端还是小端
在网络编程时,需要将 host byte order
转为 network byte order
。
rust 本身支持在编译期检查当前的编译工具链是大端还是小端,如下所示:
pub fn is_little_endian() -> bool {
cfg!(target_endian = "little")
}
当然,也有方法可以在运行期进行检查,这里我们使用 union
类型,为了方便。
pub fn is_little_endian() -> bool {
union Check {
l: u16,
s: [u8; 2],
}
unsafe {
let c = Check { l: 0x0102 };
return c.s[0] == 2;
}
}
但要注意的是直接访问 union
中的内部元素是 unsafe
的。
rust 中 string 与其它类型间的互转
Rust 中经常用到的字符串与其它数据类型之间的转换, 可以通过其它类型实现几个预定义的 Trait 来完成.
其中, 包括:
std::str::FromStr
, 用于从字符串转为指定的类型, 提供了parse()
方法std::string::ToString
, 用于将将指定的类型转为字符串, 提供了to_string()
方法
在标准库中, 已经实现了基本数据类型(比如bool, i32, u64, f64等)与字符串之间的互转, 举例:
let i = "42".parse::<i32>();
assert_eq!(i, Ok(42));
以及:
let s = 3.14.to_string();
assert_eq!("3.14", s);
下面以 struct Color
为例, 说明一下如何为自定义的类型实现以上两个 Traits.
windows 系统中配置 rust 环境
与 linux 系统相比, 在 widnows 系统中设置 rust 开发环境要麻烦一些. 需要手动额外安装几个软件包.
- C++ build tools for Visual Studio
- git (可选)
- .Net Framework (>= 4.5)
Rust cmp 数值比较模块中的PartialEq, PartialOrd
core::cmp.rs
模块里定义了用于两值之间比较的几个 trait, 分别是:
- PartialEq
- Eq
- PartialOrd
- Ord
这四个 trait 之间有这样一个关系:
- Eq 基于 PartialEq, 即
pub trait Eq: PartialEq
- PartialOrd 基于 PartialEq, 即
pub trait PartialOrd: PartialEq
- Ord 基于 Eq 和 PartialOrd,
pub trait PartialOrd: Eq + PartialOrd<Self>
同时还定义了比较结果 Ordering
这样一个枚举类型:
pub enum Ordering {
Less = -1,
Equal = 0,
Greater = 1,
}
Rust 中的 Drop Trait
Rust 中的引用与借用 (references and borrowing)
Rust 中, 引用, 简单而言就是指向一个值所在内存的指针, 这个跟 c++ 里是一致的. 引用本身不支持指针操作, 使用起来更安全.
Cargo 使用 build.rs 动态生成版本号
最近有个需求, 要在生成的可执行文件中包含一个版本号字段, 除了像 0.1.0
这样的
在 Cargo.toml
中定义的版本号之外, 最好还包含一下当前的 git commit id.
这个功能可以用 build.rs 脚本来实现. 大致做法也很简单:
- 在 build.rs 中读取 Cargo.toml 里定义好的版本号, 以及当前的 git 历史, 并写入到编译目录里的一个文本文件
- 在
src/
目录的源代码里, 定义一个字符串常量, 它的值在编译期指定的, 就是这个文件文件的内容
在 docker 中编译 rust 程序
为了保证一致的编译环境, 可以考虑把代码放在 docker 中编译.
做法也很简单, 下面以之前用到的 musl-demo
示例程序为例来说明一下.
使用 musl 库静态编译 rust 程序
rust 提供了很简单的方式调用外部的 C 库, 但是它默认是以动态链接的方式链到外部库, 这增加了部署时的复杂性.
musl 是一个轻量级的 libc 库, 与 glibc 相比, 它的代码比较简洁, 体积也更小, 更方便 静态编译到程序中.
我们可以将 musl 以及第三方的 C 库静态链接到 rust 程序可执行文件中.
减小 rust 编译出的程序的文件大小
随着引入的依赖包越来越多, 编译生成的 rust 程序二进制文件越来越大, 要花更久的 时间把它推送到线上服务器.
通过使用以下手段, 可以显著减小生成的程序文件大小, 同时不破坏其完整性.
debug 模式
debug 模式生成的文件非常大, 里面包含了很全面的调试信息, 也没有经过优化.
常用的 cargo 命令以及扩展命令
熟练 cargo 工具以及扩展之后, 编写 rust 软件可以更高效.
cargo 自带命令
cargo update
更新 Cargo.lock
中定义的依赖包的版本.
Rust 中的 never 类型
这个特性目前还只有在 nightly
版本中可用, 但我觉得这是一个很差劲的设计.
pub fn exit(status: i32) -> ! {
nc::exit(status);
}
Rust match 中的坑
Rust 的 match 模式匹配, 可以很方便地匹配多种情况, 类似于 C 语言中的 switch 语句. 但是, 如果要匹配数字范围时, 需要这样写:
Rust 抓取 panic 并恢复运行
长期运行的服务, 比如 web app 服务器, 需要抓取 panic, 防止因为处理一个请求出错 导致整个服务退出.
可以使用 panic 库来修改抓取异常退出. 示例如下:
Rust 中使用 Raw identifier
Rust 中定义的关键字越来越多, 有时可能会有命名冲突, 为了部分缓解这个问题, 在
Rust 2018 中定义了 Raw Identifier
. 先看示例:
为 cargo 和 rustup 设置内地的代理
cargo
如果在大陆使用 cargo 命令下载依赖包太慢, 可以使用中科大的官方镜像, 只需要在
$HOME/.cargo/config
或者项目的.cargo/config
里面加入以下内容即可:
[source.crates-io]
registry = "https://github.com/rust-lang/crates.io-index"
replace-with = 'ustc'
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
Rust 语言中的瑕疵 - 省去 return
Rust 语法中的 return
关键字是可以省去的, 这也是推荐做法,
但是某些条件下会带来问题.
先看例子:
fn max(a: i32, b: i32) -> i32 {
if a > b {
a
} else {
b
}
}