早就想学 Rust 了,但一直咕着。这次小学期的程设训练开了 Rust 课堂,就毫不犹豫地选了,也以此为契机学了 Rust。
这篇就是简单记录一下大体上的学习过程,如果有什么心得体会、经验分享之类应该会开新的文章。
顺便也算是程设训练的游记了(“游记”这个词来源于 OI 时期的习惯)。
初学 —— The Rust Programming Language
不同于 Python、JavaScript,这次学 Rust 更像是当初学 C++,不是先魔改代码、用啥搜啥、StackOverflow,而是在还没怎么接触过时就直接看书。
虽然没有细看中文翻译的质量如何,但至少术语是不好翻译的,就直接看了英文版的 The Rust Programming Language。
这本书看下来给我的感觉是,很多地方没有按知识点依赖关系的拓扑排序来讲,而似乎是以某种由浅入深的顺序来的,很多后面才学到的概念在前文就出现,而前面学的概念的完全不需要后面知识的扩展又在后面才补充,还有一些零碎的知识是在 project chapter 讲的。
反正,每天看两三章,还能有不少时间摸鱼,摸个 一两周就看得差不多了(
初步练习 —— 洛谷
看书的同时在 洛谷 上随便挑了点入门题和板子题写。只不过一开始没学 io 之类的,会再多语法也做不动题,但幸好有 GitHub Copilot。
在这放个读入的例子:
6 0 1 1 4 5 1 4
fn main() {
let stdin = std::io::stdin();
let mut input = String::new();
stdin.read_line(&mut input).unwrap();
let mut parts = input.split_whitespace();
let n = parts.next().unwrap().parse::<u32>().unwrap();
assert_eq!(n, 6);
let m = parts.next().unwrap().parse::<u32>().unwrap();
assert_eq!(m, 0);
input.clear();
stdin.read_line(&mut input).unwrap();
let a = input
.split_whitespace()
.map(|x| x.parse::<i32>().unwrap())
.collect::<Vec<_>>();
assert_eq!(a, vec![1, 1, 4, 5, 1, 4]);
}
复习 —— A half-hour to learn Rust
之前在 Hacker News 上看到了 A half-hour to learn Rust 这篇文章。如果真的是看这个东西来学 Rust,不说能不能学会,肯定是学不扎实的。但是,看完书之后很多语法也忘了,看这个用来复习还是非常不错的。
深入了解 —— The Rust Standard Library
其实看书的时候就感觉到了,很多时候不懂一段代码不是不懂语法,而是不懂 标准库 的实现,而把 API 理解成了没学过的语法。了解标准库,不仅是提升编码和运行的效率,也是能够看懂很多基础代码的关键。
看文档的时候发现,不仅是学到了很多有用的 methods,也学到了很多其它东西。比如说:
- 一般来说都可以用
std
来取 max,但:: cmp :: max f32
专门有一个pub fn max(self, other: f32) -> f32
,这是因为浮点数有 NaN 这个特殊情况导致f32
没有 implOrd
,而std
需要:: cmp :: max Ord
- 书里貌似没讲到的 keyword
ref
(以及在 pattern matching 中和&
的区别) - 引用之间进行比较时会自动转成指向的值,即实际进行比较的是指向的值(可以通过
std
来比较地址):: ptr :: eq - 从标准库的 API 设计能领会到很多 trait 和 generic 的用法,体会到它们组合在一起的强大
Iterator
自动有IntoIterator
,所以用IntoIterator
代替Iterator
作为 trait bound 可以让函数更灵活- ……
简单看一看就能知道 the book 里涉及到的真的只是冰山一角,学 Rust 标准库的意义会比学 C++ 的 STL 大得多(个人感觉)
上课
Class 1
因为自学过了,所以上课就基本上是把老师的声音当作 bgm 偶尔听一下,然后继续看标准库文档(
上课的时候老师提到 Rust 编译器可以提示如何修改,于是我顺势在课程群里发了个 rust 程序员现状 的截图(
课后把 OJ 上的作业速通后发现榜不是公开的 😢
后来还发现自己一道题写了个 collect
得到的 Vec
只用在一个 for
里(就是说可以直接把迭代器用在 for
不用先 collect
)😵 想改过来,但虽然没有公开榜,还是不想承受可能的罚时(
Class 2
第二节课前后共一天多的时间,写了 16h 左右,把 Wordle 大作业的基础功能写完了。本来以为 Wordle 挺好写的,没想到需求这么多,需求文档模糊不清的地方还有的要问有的要自己设计。)
感觉课上讲的又快,大作业需要的知识又没讲全,很难想象如果不自学该怎么应对这个课(
Class 3
修了一些文档说明不清导致的 bug,然后研究了一下扩展功能怎么写。
不知道为什么把单词按信息熵排序写了半天,明明挺简单的。可能是听着老师讲课没法专心想算法。
后来试着用 rayon 把信息熵计算并行化了,在我本机除了第一次猜测基本上都能秒出结果,感觉很爽 ,甚至玩了半天意义不明的照着提示输入。
再后来发现用 release 模式编译的优化比并行还大 🌚
第一次大作业验收
验收在 THUWC 去过的东主楼,但上大学后还是第一次去,结果走错了,迟到了 5min 😵 然后非常慌张地展示提高功能,都没太演示全,感觉白写了(
Class 4~8
略(
第二次大作业验收
这次真的有比 pretests 强很多的 system test 了,还好没 FST(助教:这个点你为什么能过啊(x
可能是因为这个课不是学前端的,而且助教只简单看了一下界面演示没看代码,槽点很多的前端被夸了很科学(
可能是因为这个课不是学数据库的,而且听说有其他同学数据库里全是 JSON,普普通通的建表被夸了很科学(
关于课上的一些翻译
为什么什么都要翻译出来啊(虽然如果英语母语的话看原文应该也很尬 🤣
大作业代码
听说可以把代码公开 加速熔断,这就放上来(
https
Async Rust
先是看了 Asynchronous Programming in Rust,感觉看得一知半解的(尤其是 Pin
),好像大概知道 Future
在干什么又不完全知道(而且这本书好咕啊,TODO
的章节应该是有生之年了
没看完 async book,直接去看 Tokio Tutorial 和 Async programming in Rust with async-std 了。作为 async runtime 的教程,它们涉及到的具体原理和实现少一些,更注重怎么实际使用,读起来会容易理解一些。因为 Tokio 更 popular,主要看的是 Tokio 的教程。
数据库: Diesel
感觉 diesel 的教程 写的挺简略的,但对着 examples 硬查文档也勉强能看懂基本用法。ORM 看着就很“安全”,只不过实在是太类型体操了,不仅文档查起来有点小麻烦,代码复用也经常因为繁琐的 trait bounds 写不太动(也可能是我没学会)。
diesel-derive-enum 是好用的。
Sqlite 的 RETURNING
语句需要启用 returning_
feature flag(并且需要至少 3.35 版本的 Sqlite)。没启用的时候对着一堆 trait bound 不满足的错误信息(就和 C++ 模板感觉差不多了..)根本发现不了错误原因,还是翻文档翻半天发现的。
还遇到一个 Sqlite 锁死的坑,通过 使用 r2d2 设定 busy_timeout
并 使用 WAL mode 以及 immediate transaction (hopefully)解决了。