大家好,我是梦兽编程。点进来的多半是因为看到谷歌的字眼吧。没错Rust系列将以谷歌安卓团队分享的Rust经验进行持续更新。
这是由 Google 的 Android 团队开发的免费 Rust 课程。本课程涵盖了 Rust 的方方面面,从基本语法到泛型和错误处理等高级主题。
该课程的最新版本可以在 https://google.github.io/comprehensive-rust/找到。如果您正在其他地方阅读,请检查那里的更新。
在梦兽编程开了这么多rust的教程中,谷歌安卓团队推出的rust经验分享,可以说是简单明了。每个分享都是突出重点没有一句多余的废话。新手入门也可以很容易的掌握rust的“生命周期”,“借用”,“引用”。
但是谷歌安卓团队这个分享都是一些PPT内容,这里就由梦兽编程详细的补充。
如果你之前没有了解过Rust,本次Rust专栏分享希望你可以做到以下几点:
- 让你全面了解Rust的语法和语言
- 能够阅读或参与GitHub上优秀的Rust项目
什么是Rust
Rust是一门新的编程语言,最早是2015年发布了1.0的版本。Rust是静态编译语言与C++类似。Rust使用LLVM 作为作为工具链集合。Rust和C++类似也能运行在各大平台,而且也能运行在嵌入式应用中。
Rust与C++一样没有运行时和垃圾回收,代码中的内存交由开发者进行管理。所以Rust的性能几乎与C++一致。只不过C++的性能取决编写代码的人能力,如果C++水平不高的人写出来的代码依然是很慢的。
Rust作为一门21世纪的新语言,它很多时候都是由编译器帮我们处理好了,Rust的性能85%的情况下是有编译器帮我们处理的。正因为编译器帮我们做了很多活,Rust同时也帮我们处理C++大多数头条的指针问题
Rust 注重可靠性和安全性,同时不牺牲性能。
Hello Word
让我们跳到最简单的 Rust 程序,一个经典的 Hello World 程序:
fn main() {
println!(""Hello 🌍!"");
}
在这个例子中,我们使用fn这个关键词定义rust语言中的函数。几乎所有的静态编译语言都会存在一个main的主入口函数Rust也不其然。
println!是Rust语言提供的宏,宏这个概念在C中并不陌生。一般带有GC的语言是不提供这种功能。println!与GC语言(比如Golang中的fmt是两个概念)。Rust 在您希望拥有可变数量的参数(无函数重载)的情况下使用宏。
宏是不容易被污染的,这意味着它们不会意外地捕获来自它们被使用的范围的标识符。这使得 Rust 中的宏更加安全和可靠。
Rust 是多范式的。例如,它具有强大的面向对象编程功能,并且虽然它不是一种函数式语言,但它包含了许多函数式概念。
梦兽编程是比较喜欢使用函数式的,这个看个人习惯。
一个小例子进阶
fn main() { // Program entry point
let mut x: i32 = 6; // Mutable variable binding
print!("{x}"); // Macro for printing, like printf
while x != 1 { // No parenthesis around expression
if x % 2 == 0 { // Math like in other languages
x = x / 2;
} else {
x = 3 * x + 1;
}
print!(" -> {x}");
}
println!();
}
// output 6 -> 3 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1
在这个例子中,我们如果将代码中的let mut x :i32 = 6;
修改改成 let x:i32 = 6
;这个程序会立马报错,在rust中如果没有mut这个关键字。那么rust会认为这个变量不是一个可变的参数,而是一个常量。定义是不可以进行修改的。
在上述的代码中print!(" -> {x}");
同样可以修改成print!(" -> {}",x);
的写法。
值得注意的是你需要明确{}的占位符,如果修改成print!(" -> {}",x,y);
同样是无法进行编译的。
在上述代码中我们吧i32改成i8,并把 = 6的值修改到1000同样会报错,这里需要注意类型的取值范围,与大多数静态编译语言类似。
在开发的过程中,比如print是在std::fmt的package下,如果你想查看更多关于std::fmt的内容可以在控制台中执行rustup doc std::fmt
,运行后会跳到浏览器客户端查看详细的文档,这个过程中需要你有一点英语能力。
rustup doc std::fmt
为什么使用Rust
Rust的独特特点:
- 编译时内存安全。
- 缺少未定义的运行时行为。
- 现代语言功能。与C和C++的CMake工具链相比Rust的工具链相当优秀。当然C++也有比较好的解决方案(微软解决方案,当需要你在VS开发工具使用只能在window上进行开发)
如果你有c和c++开发经验,那么它们的语法特性让你写出空指针的情况是非常多的。就golang来说你也有可能在运行时写出空指针。但Rust通过借用编译器消除了一整类运行时错误,让你拥有与c、c++媲美的性能,但你内存不安全问题。此外,您还可以获得一种现代语言,其中包含模式匹配和内置依赖项管理等构造。
对于Java,Go,Python,JavaScript...的经验:您可以获得与这些语言相同的内存安全性,以及类似的高级语言感觉。此外,您还可以获得快速且可预测的性能,如 C 和 C++(无垃圾回收器)以及对低级硬件的访问,如果你有嵌入式开发的需求。
打个比方
我们查看最小错误示例的c代理例子,你能看出多少错误?
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
int main(int argc, char* argv[]) {
char *buf, *filename;
FILE *fp;
size_t bytes, len;
struct stat st;
switch (argc) {
case 1:
printf("Too few arguments!\n");
return 1;
case 2:
filename = argv[argc];
stat(filename, &st);
len = st.st_size;
buf = (char*)malloc(len);
if (!buf)
printf("malloc failed!\n", len);
return 1;
fp = fopen(filename, "rb");
bytes = fread(buf, 1, len, fp);
if (bytes = st.st_size)
printf("%s", buf);
else
printf("fread failed!\n");
case 3:
printf("Too many arguments!\n");
return 1;
}
return 0;
}
虽然只有29行,但是这里的代码中存在9个致命的错误。
- 赋值 = 而不是相等比较 == (第 28 行)
- (第 23 行)的 printf 多余参数
- 文件描述符泄漏(第 26 行之后)
- 多行 if 中被遗忘的大括号(第 22 行)
- 在 switch 声明 break 中被遗忘(第 32 行)
- 忘记字符串的 buf NUL 终止,导致缓冲区溢出(第 29 行)
- 通过不释放 分配的 malloc 缓冲区而导致内存泄漏(第 21 行)
- 越界访问(第 17 行)
- switch 语句中未选中的案例(第 11 行)
- stat 未经检查的返回值 and fopen (第 18 行和第 26 行)
即使对于 C 编译器来说,这些错误也不应该很明显吗?出乎意料的是,这段代码在最新版本的 GCC(截至写作时为 13.2)中,即使在默认警告级别下也能编译为无警告。
这不是一个非常不切实际的例子吗?
绝对不是,这些错误在过去会导致严重的安全漏洞。一些例子:
- 赋值 = 而不是相等比较 == :The Linux Backdoor Attempt of 2003
- 多行 if 中被遗忘的大括号:The Apple goto fail vulnerability
- break 在声明 switch 中被遗忘:The break that broke sudo
Rust 在这里如何更好?
以上C语言的代码中,我们可以看出很多时候因为我们的不注意导致我我们肉眼看不出来,rust为人让代码看起来更加直观做出以下调整:
- 不支持 if 子句内的赋值。
- 格式字符串在编译时检查。
- 资源在范围结束时通过 Drop 特征释放。这里需要知道作用域后释放,这对你学习生命周期时候有很多帮助
- 所有 if 子句都需要大括号。
- match (因为 Rust 等效于 switch )不会掉落,因此您不会意外忘记 break ,Rust中推荐使用match而不是switch
- 缓冲区切片具有其大小,不依赖于 NUL 终止符。
- 堆分配的内存在相应 Box 内存离开作用域时通过 Drop 特征释放。
- 越界访问会导致恐慌,或者可以通过切片 get 方法进行检查。
- match 要求处理所有案件。也就是所有的情况的都需要匹配上,包括后面学习到的None
- 易出错的 Rust 函数会返回 Result 值,该值需要被拆包并检查是否成功。此外,如果忘记检查标有 #[must_use] 属性的函数的返回值,编译器会发出警告。
好了今天先将这么多,如果你非常喜欢本栏目可以进入https://rexweb.link/category/technical-column/learn-rust-from-the-android-team或者更多技术可以收藏梦兽编程https://rexweb.link
梦兽编程倔强号
梦兽编程知乎
梦兽编程bilibili
微信搜索梦兽编程公众号
梦兽编程倔强号
https://juejin.cn/user/2066737588876983
梦兽编程知乎
https://www.zhihu.com/people/mo-dong-74
梦兽编程bilibili
https://space.bilibili.com/106325238?spm_id_from=333.1007.0.0
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。