大家好,我是砸锅。一个摸鱼八年的后端开发。熟悉 Go、Lua。第三天还是继续和大家一起学习 Rust😊
代码缺陷
从代码开发的角度看,一个软件系统想要提供良好用户体验的功能,最基本的要求就是控制缺陷:
- 语法缺陷 → RLS / Rust Analyzer
- 类型安全缺陷 → 类型系统
- 内存和资源安全缺陷 → 所有权、借用检查、生命周期 Rust 通过所有权、借用检查和生命周期检查,来保证内存和资源分配之后,在其生命周期结束之后就会被释放掉
- 并发安全缺陷 → 所有权、借用检查、生命周期 + 类型系统
- 错误处理缺陷 → 编译器告警 Rust 使用 Result <T,E> 类型来保证错误的类型安全,还强制要求必须处理这个类型返回的值,避免开发者丢弃错误
- 代码风格和常见错误引发的缺陷 → cargo fmt / cargo clippy
- 逻辑缺陷 → 单元测试
- 功能缺陷 → 集成测试
- 用户体验缺陷 → 端到端测试 / 手工测试
Rust 准备
安装 Rust
curl --proto '=https' --tlsv1.2 -sSf <https://sh.rustup.rs> | sh
开发工具
任何编辑器都可以撰写 Rust 代码,推荐使用 VS Code,因为免费而且速度快。先安装以下插件:
- rust-analyzer,实时编译和分析你的 Rust 代码,提示代码中的错误
- rust syntax,为代码提供语法高亮
- crates,分析当前项目的依赖是否为最新版本
- better toml,Rust 使用 toml 做项目配置管理,better toml 可以提供语法高亮,提示 toml 文件的错误
- rust test lens,快速运行某个 Rust 测试
- Tabnine,基于 AI 的自动补全,可以更快地撰写代码
基本概念
cargo 是 Rust 用來做依賴管理以及开发过程中的任务管理,比如编译、运行、测试、代码格式化等
从一个网页里获取内容并保存成 md 的例子看看 Rust 的整体语法:
// 访问命名空间或者对象的静态函数要使用双冒号 :: 运算符
// 要简化对命名空间内部函数或者数据类型的引用可以使用 use 关键字
use std::fs;
fn main () { // 可执行体的入口函数是 main() , 函数体用花括号 {} 包裹
let url = "<https://www.rust-lang.org/>";
let output = "rust.md"; // 表达式之间用分号 ;分隔
println!("Fetching url: {}", url);
// 访问结构体的成员函数或者变量使用 . 运算符
let body = reqwest::blocking::get(url).unwrap().text().unwrap();
println!("Convering html to markdown...");
let md = html2md::parse_html(&body);
fs::write(output, md.as_bytes()).unwrap();
println!("Converted markdown has been saved in {}.", output);
}
Rust 的变量默认是不可变的,它符合最小权限原则 (Principle of Least Privilege),帮助我们写出健壮而且正确的代码。 如果要修改变量的值,需要显式使用 mut 关键字
除了 let / static / const / fn 等少数语句外,Rust 绝大多数代码都是表达式 (expression)。所以 if / while / for / loop 都会返回一个值,函数最后一个表达式就是函数的返回值
Rust 支持面向接口编程和泛型编程
Rust 支持类型推导,在编译器能够推导类型的情况下,变量类型一般可以省略,但是常量 (const) 和静态变量 (static) 必须要声明类型
Rust 拥有非常丰富的数据类型和强大的标准库
Rust 还有丰富的控制流程,包括模式匹配
Rust 函数参数的类型和返回值必须要显式定义,如果没有返回值可以省略,返回 uint;函数内部需要提前返回的话,就使用 return 关键字,否则最后一个表达式就是其返回值;如果最后一个表达式后面加了 ;分号,则隐含其返回值为 uint
数据结构
// enum 枚举类型
#[derive(Debug)]
enum Gender {
Unspecified = 0,
Female = 1,
Male = 2,
}
// struct 特殊形式,称为元组结构体,它的域都是匿名,可以用索引访问,适用于简单的结构体
#[derive(Debug, Copy, Clone)]
struct UserId(u64);
// 标准结构体
#[derive(Debug)]
struct User {
id: UserId,
gender: Gender,
}
// 定义标签联合体 (tagged union), 定义三种事件
#[derive(Debug)]
enum Event {
Join(UserId, TopicId)
Leave(UserId, TopicId)
Message((UserId, TopicId, string)),
}
一般用 impl 关键字为数据结构实现 trait,Rust 提供了派生宏 (derive macro) ,用来简化标准接口定义,例如 #[derive(Debug)] 为数据结构实现了 Debug trait,提供了 debug 功能,这样就可以通过 {:?} 来用 println! 打印
#[derive(Debug, Copy, Clone)] 里面有 Copy / Clone 两个派生宏,Clone 让数据结构可以被复制,而 Copy 则让数据结构可以在参数传递的时候自动按照字节拷贝
控制流程
顺序执行就是常说的串行执行顺序,也就是一行行代码往下执行
函数调用就是在代码执行过程中,调用了另一个函数,跳入其上下文中执行,直到返回
Rust 支持死循环 loop,条件循环 while,迭代器循环 for。也可以利用 break 提前终止,continue 跳到下一个循环
学习资料
官方的 Rust book ,入门最权威的资料
Rust 死灵书,讲述 Rust 的高级特性
Rust 小练习,巩固知识和概念的理解