AndroidPoet/rust-for-kotlin-devs
GitHub: AndroidPoet/rust-for-kotlin-devs
一份面向 Kotlin 开发者的 Rust 完整入门指南,通过代码对照和可运行示例帮助快速掌握 Rust 核心概念。
Stars: 48 | Forks: 1
# Rust for Kotlin 开发者 —— 完整指南
📖 作为可搜索的文档站点阅读 →
**可运行的配套代码:** 每个概念在 [`examples/`](examples) 中都有一个可运行且通过 clippy 检查的文件。克隆仓库并试一试:
**30 秒摘要:** Rust 的感觉就像是 ~50% 的 Kotlin 加上 50% 的新思维方式。你仍然可以使用类型推断、密封类风格的 enum、模式匹配、lambda、泛型、默认不可变性,以及用 `Option`/`Result` 代替 `null` + 异常。真正全新的是:**没有垃圾回收器。** 取而代之的是,编译器会追踪每个值的*所有权*和*借用*,以及每个引用的*生命周期*。这里没有类继承——你通过组合 *trait* 来实现行为。你早期的挣扎大部分在于**借用检查器**,而不是语法。语法你一天就能学会;而所有权模型则需要一两周的时间。
| Kotlin 习惯 | Rust 现实 |
|---|---|
| `val` / `var` | `let` / `let mut` —— 绑定**默认**是不可变的,使用 `mut` 开启可变性 |
| GC 为你清理内存 | **所有权** —— 每个值都有唯一的所有者;当所有者离开作用域时释放。没有 GC。 |
| 自由传递对象(共享引用) | **借用** —— 传递 `&T`(共享)或 `&mut T`(独占);由编译器强制执行规则 |
| `fun foo(): Int` | `fn foo() -> i32` —— `fn` 关键字,返回类型在 `->` 之后 |
| `null` + `?.` `?:` `!!` | **没有 null。** `Option
` = `Some(x)` / `None`,通过 `match`、`?`、`.unwrap()`、`if let` 解包 |
| 异常 + `try/catch` | **没有异常**(针对可恢复的错误)。`Result` = `Ok`/`Err`,通过 `?` 传播 |
| `class` + 继承 (`open`/`override`) | 使用 `struct` 存储数据,**没有继承** —— 通过 `trait` 共享行为(类似于带默认方法的接口) |
| `sealed class` / `sealed interface` | `enum` —— Rust 的 enum 每个变体都可以携带数据;这是最核心的类型 |
| `interface` | `trait` —— 但它同时兼具泛型约束、扩展函数和运算符重载的功能 |
| `when` | `match` —— 穷尽的、基于模式的表达式。非常相似,但更强大。 |
| 扩展函数 | `impl` 块 + trait(`impl Trait for Type`) |
| `data class` | `#[derive(Clone, Debug, PartialEq)] struct` —— 按需派生 |
| 协程 + `suspend` | `async`/`.await` + 一个 runtime(`tokio`)—— 形式类似,由你选择执行器 |
| 分号可选 | **分号很重要**:带 `;` 的是语句,不带 `;` 的是返回表达式 |
## 1. 变量
```
// Kotlin // Rust
val x = 5 let x = 5; // immutable by default
var y = 10 let mut y = 10; // opt into mutation with `mut`
y = 20 y = 20;
val name: String = "Ada" let name: String = "Ada".to_string();
```
有两件事会让 Kotlin 开发者感到惊讶:
- **变量遮蔽是惯用法。** 你可以用新的 `let` 重新声明相同的名称,甚至更改它的类型。这不是修改——而是一个新的绑定。
```
let spaces = " "; // &str
let spaces = spaces.len(); // now usize — totally fine, not `mut`
```
- **`const` 仅限编译期**并且需要指定类型。全局变量还可以用 `static`。它们都不是你的日常工具——日常请用 `let`。
```
const MAX_POINTS: u32 = 100_000;
```
## 2. 内置类型
Rust 对整数宽度和有无符号非常明确。没有单一的 `Int`。
| Kotlin | Rust |
|---|---|
| `Int` | `i32`(默认整数) |
| `Long` | `i64` |
| `Short` / `Byte` | `i16` / `i8` |
| `UByte` / `UShort` / `UInt` / `ULong`(无符号,自 1.5 起稳定) | `u8` / `u16` / `u32` / `u64` —— 外加 `usize`(索引/长度类型,Kotlin 中无直接对应) |
| `Float` / `Double` | `f32` / `f64`(默认浮点数) |
| `Boolean` | `bool` |
| `Char` | `char`(完整的 Unicode 标量,4 字节 —— 不是 UTF-16 单元) |
| `String` | `String`(拥有所有权,可增长)**以及** `&str`(借用的字符串切片) —— 见 §3 |
| `List` | `Vec`(可增长)和 `[T; N]`(固定大小数组),`&[T]`(切片) |
| `Map` | `HashMap`(来自 `std::collections`) |
| `Pair`/`Triple` | 元组:`(i32, String)`、`(a, b, c)` |
| `Unit` | `()`(单元类型) |
| `Nothing` | `!`(never 类型) |
```
let sum: i64 = 1_000_000 * 2;
let tuple: (i32, f64, char) = (500, 6.4, 'z');
let (a, b, c) = tuple; // destructuring, like Kotlin
let first = tuple.0; // tuple index access
```
整数溢出**在 debug 构建中会 panic**,而在 release 构建中会环绕(wrap)—— 当你确实需要时,请使用 `wrapping_add`、`checked_add`、`saturating_add`。
## 3. 字符串 —— 总是让人绊倒的类型
Kotlin 只有一个 `String`。Rust 有两个你会经常用到的类型:
- `String` —— 拥有所有权、分配在堆上、可增长。类似于 `StringBuilder` 和 `String` 的结合体。
- `&str` —— 对字符串数据的*借用*视图(一个“字符串切片”)。字符串字面量就是 `&str`。
```
// Kotlin // Rust
val s = "hello" let s: &str = "hello"; // literal is &str
val owned = buildString { ... } let owned: String = "hello".to_string();
let owned = String::from("hello");
```
将 `&str` 作为函数参数(可以同时接受两者),当你拥有数据所有权时返回 `String`。
**字符串插值**看起来很熟悉,但默认只接受简单的变量名:
```
let name = "Ada";
let age = 36;
println!("{name} is {age}"); // inline capture (Rust 1.58+, any edition)
println!("{} is {}", name, age); // positional
let msg = format!("{name} is {age}"); // returns a String
```
按整数索引(`s[0]`)是**不允许的** —— 因为在 UTF-8 中,字节、字符和字素变得非常不明确。请改为迭代:
```
for c in "héllo".chars() { /* ... */ }
let bytes = "hi".as_bytes();
```
## 4. 函数
```
// Kotlin // Rust
fun add(a: Int, b: Int): Int { fn add(a: i32, b: i32) -> i32 {
return a + b a + b // no `;`, no `return` — last expression is returned
} }
```
**Rust 中最重要的语法思想:** 代码块是表达式。不带分号的最后一行就是代码块的返回值。
```
let y = {
let x = 3;
x + 1 // no semicolon → this block evaluates to 4
};
```
`return` 存在,但主要用于提前退出。其他情况直接从底部“掉出来”。
闭包(lambda):
```
// Kotlin: { a, b -> a + b }
let add = |a, b| a + b;
let nums: Vec = (1..=5).map(|x| x * 2).collect();
```
## 5. 没有 null —— `Option`
Rust 没有 `null`。缺失的状态是 `Option` 类型的一个值:
```
enum Option { Some(T), None }
```
```
// Kotlin // Rust
val name: String? = null let name: Option = None;
val name: String? = "Ada" let name: Option = Some("Ada".to_string());
```
处理它 —— 映射到 Kotlin 习惯的几种符合人体工程学的工具:
```
// Kotlin: name?.length // map over Some
let len: Option = name.map(|n| n.len());
// Kotlin: name ?: "default" // provide a fallback
let n = name.unwrap_or("default".to_string());
let n = name.unwrap_or_else(|| expensive());
// Kotlin: name!! // assert non-null (panics if None)
let n = name.unwrap(); // or .expect("name must be set")
// Kotlin: if (name != null) { use(name) } // smart-cast style
if let Some(n) = &name {
println!("{n}");
}
```
在返回 `Option` 的函数内部,`?` 也可以作用于 `Option` —— 提前返回 `None`。
## 6. Struct 与“构造函数”
没有 `class`。数据存放在 `struct` 中;行为存放在 `impl` 块中。
```
// Kotlin
data class User(val name: String, var age: Int)
// Rust
#[derive(Debug, Clone)]
struct User {
name: String,
age: u32, // fields are private to the module by default; add `pub` to expose
}
impl User {
// associated function = "constructor" by convention, named `new`
fn new(name: String, age: u32) -> Self {
User { name, age } // field init shorthand, like Kotlin
}
// method: takes `&self` (borrow), `&mut self` (mutable borrow), or `self` (consume)
fn greet(&self) -> String {
format!("Hi, I'm {}", self.name)
}
fn have_birthday(&mut self) {
self.age += 1;
}
}
let mut u = User::new("Ada".into(), 36);
println!("{}", u.greet());
u.have_birthday();
```
与 Kotlin 的主要区别:
- 没有主构造函数的语法糖 —— 你需要写 `fn new`。这只是约定,不是关键字。
- `self`/`&self`/`&mut self` 是显式的并且**很重要**:它声明了该方法是读取、修改还是消耗接收者。
- 其他 struct 形式:**元组 struct** `struct Point(i32, i32);` 和**单元 struct** `struct Marker;`。
## 7. `data class` → derive 宏
Kotlin `data class` 的免费功能(`equals`、`hashCode`、`toString`、`copy`)可以通过 `#[derive(...)]` 来选择启用:
```
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct Point { x: i32, y: i32 }
let a = Point { x: 1, y: 2 };
let b = a.clone(); // ~ Kotlin copy() (clone is the general mechanism)
assert_eq!(a, b); // PartialEq gives ==
println!("{a:?}"); // Debug gives a printable form ({:?})
// struct update syntax ≈ copy(x = 9)
let c = Point { x: 9, ..a };
```
| Kotlin data class 提供的功能 | Rust derive |
|---|---|
| `equals`/`hashCode` | `PartialEq, Eq, Hash` |
| `toString` | `Debug` (`{:?}`) 和/或 `Display`(手写,`{}`) |
| `copy()` | `Clone` + struct 更新语法 `{ ..old }` |
| `componentN` 解构 | 模式解构 `let Point { x, y } = p;` |
## 8. Enum —— 你一直想要的密封类
Kotlin 的 `sealed class` 和 Rust 的 `enum` 是同一个概念,但 enum 是 Rust 的*核心*类型,而且编写起来轻量得多:
```
// Kotlin
sealed interface Shape
data class Circle(val r: Double) : Shape
data class Rect(val w: Double, val h: Double) : Shape
object Empty : Shape
```
```
// Rust — one declaration, variants carry data
enum Shape {
Circle { r: f64 },
Rect(f64, f64),
Empty,
}
impl Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle { r } => std::f64::consts::PI * r * r,
Shape::Rect(w, h) => w * h,
Shape::Empty => 0.0,
}
}
}
```
`Option` 和 `Result` 只是标准库中的 enum。一旦你搞懂了 enum,Rust 的大部分也就迎刃而解了。
## 9. 模式匹配 —— `when` → `match`
```
// Kotlin `when` // Rust `match` — exhaustive, an expression
val label = when (n) { let label = match n {
0 -> "zero" 0 => "zero",
1, 2 -> "small" 1 | 2 => "small",
in 3..10 -> "medium" 3..=10 => "medium",
else -> "big" _ => "big",
} };
```
`match` 必须是**穷尽的** —— 编译器强制你处理每一种情况(或者用 `_`)。它支持深度解构:
```
match shape {
Shape::Circle { r } if *r > 10.0 => println!("big circle"), // guard
Shape::Rect(w, h) => println!("{w}x{h}"),
other => println!("{:?}", other),
}
```
针对单一情况的轻量级形式 —— 替代 Kotlin 的 `if` 智能转换:
```
if let Some(x) = maybe { use_it(x); }
let Some(x) = maybe else { return; }; // let-else: bind or bail
while let Some(item) = stack.pop() { /* ... */ }
```
## 10. 所有权 —— 真正全新的概念
这个概念在 Kotlin 中没有对应物。请慢慢阅读。
**规则 1:每个值都有且仅有一个所有者。** 当所有者离开作用域时,该值被丢弃(释放)。没有 GC,默认也没有引用计数。
```
let s1 = String::from("hi");
let s2 = s1; // ownership MOVES to s2
// println!("{s1}"); // ❌ compile error: s1 was moved, it's no longer valid
```
对于堆类型(`String`、`Vec`、装箱数据),赋值/传递是**移动**,而不是拷贝。`s1` 会失效,这样两个所有者就不会同时释放它。小型的 `Copy` 类型(`i32`、`bool`、`char` 以及由它们组成的元组)会被*拷贝*,所以感觉很正常:
```
let a = 5;
let b = a; // copied — both a and b are valid
```
**传递给函数也会导致移动**,除非你借用(下一节)或调用 `.clone()`:
```
fn consume(s: String) { /* s dropped here */ }
let s = String::from("hi");
consume(s);
// s is gone now; use consume(s.clone()) to keep a copy
```
心智模型:将每个非 `Copy` 的值看作一个独占资源(文件句柄、互斥锁)。你肯定不希望两个所有者同时去关闭它。
## 11. 借用与引用 —— 传递而不放弃所有权
到处移动会很痛苦,所以你通过引用来**借用**。有两种类型:
```
fn len(s: &String) -> usize { s.len() } // & = shared/immutable borrow
fn push(s: &mut String) { s.push('!'); } // &mut = exclusive/mutable borrow
let mut s = String::from("hi");
let n = len(&s); // lend a read-only view; s still owned here
push(&mut s); // lend a mutable view
```
**借用检查器规则**(大多数早期编译错误的根源):
1. 你可以拥有**任意数量的 `&` 共享借用**,或者**恰好一个 `&mut` 独占借用** —— 绝不能同时存在。
2. 引用的生命周期绝不能超过它所指向的数据(避免悬垂指针)。
```
let mut v = vec![1, 2, 3];
let first = &v[0]; // shared borrow
v.push(4); // ❌ needs &mut while `first` is still borrowing — compile error
println!("{first}");
```
这就是 Kotlin 中“迭代列表时不要修改它”的规则演变成了整个程序的编译期保证。这就是*别名异或可变性*:数据可以被共享,或者可变,但绝不能同时兼具。这也是 Rust 在结构上保证无数据竞争的原因。
当单一所有权确实行不通时(一个值在很多地方共享),你可以求助于 **`Rc`**(共享所有权,单线程)或 **`Arc`**(原子操作,线程安全),通常还会配合 **`RefCell`/`Mutex`** 实现内部可变性 —— 这最接近 Kotlin 中自由共享的引用,只是变得更加显式。
## 12. 生命周期 —— 通常是隐形的,偶尔是显式的
生命周期是编译器用来证明规则 2(无悬垂引用)的手段。大多数时候它们是**推断出来**的,你不需要写任何东西。只有当函数返回一个引用,而编译器无法判断它借用了哪个输入参数时,你才需要标注:
```
// "the returned &str lives as long as both inputs"
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
```
`'a` 是一个*生命周期参数* —— 只是一个标签,而不是你选择的时长。不要在第一天就死磕这些;当你需要它们时自然会遇到,而且错误消息通常会告诉你该怎么写。
## 13. 错误处理 —— `Result`,而不是异常
Rust 将错误一分为二:
- **不可恢复** → `panic!`(Bug、被破坏的不变性)。展开或中止。就像未捕获的异常,但你不需要围绕捕获它来进行设计。
- **可恢复** → `Result`,你必须处理的普通值。
```
enum Result { Ok(T), Err(E) }
```
```
// Kotlin // Rust
fun read(): String { /* throws IOException */ } fn read() -> Result { ... }
try { match read() {
val s = read() Ok(s) => use_it(s),
use(s) Err(e) => eprintln!("{e}"),
} catch (e: IOException) { ... } }
```
**`?` 运算符**是一个杀手级特性 —— 它就是“解包或返回错误”,将冗长的匹配转化为干净的正常执行路径:
```
fn load() -> Result {
let text = fs::read_to_string("config.toml")?; // if Err, return it now
let cfg = parse(&text)?; // same
Ok(cfg)
}
```
`?` 大致类似于 Java 的受检异常传播(Kotlin 刻意去掉了它)—— 区别在于错误是一个普通的值,在每个调用点都是可见且经过类型检查的。像 `anyhow`(用于应用程序)和 `thiserror`(用于库)这样的 crate 让错误类型变得非常符合人体工程学。
## 14. Trait 集接口、扩展和泛型约束于一体
Rust **没有继承。** `trait` 是用于共享行为的单一工具 —— 它是 Kotlin 的 `interface`(带默认方法)、扩展函数和运算符重载的结合体。
```
// Kotlin interface with default method
trait Greet {
fn name(&self) -> String; // required
fn hello(&self) -> String { // default method
format!("Hello, {}", self.name())
}
}
struct Dog;
impl Greet for Dog {
fn name(&self) -> String { "Rex".into() }
}
```
因为 trait 可以为*任何*类型实现(甚至是你没有定义的类型,比如给 `i32` 添加方法),它们同时充当了**扩展函数**:
```
trait Doubler { fn double(&self) -> Self; }
impl Doubler for i32 { fn double(&self) -> i32 { self * 2 } }
let x = 21.double(); // 42
```
值得尽早了解的常见可派生/标准 trait:`Debug`、`Clone`、`Copy`、`PartialEq`/`Eq`、`Default`、`From`/`Into`、`Iterator`、`Display`。
## 15. 泛型
与 Kotlin 几乎完全相同,带有 `where` 子句约束:
```
// Kotlin: fun > max(list: List): T
fn largest(list: &[T]) -> T {
let mut max = list[0];
for &item in list { if item > max { max = item; } }
max
}
// longer form
fn print_all(items: &[T]) where T: std::fmt::Display { /* ... */ }
```
- Trait 约束(`T: PartialOrd`)等同于 Kotlin 的泛型约束(`T : Comparable`)。
- 参数/返回位置的 `impl Trait` ≈ Kotlin 中使用接口类型而不命名泛型。
- Rust 泛型是**单态化**的(零开销,在编译期为每种类型专门生成代码)—— 没有反射,没有类型擦除。
## 16. 集合与迭代器
`Vec` 和 `HashMap` 就是你的 `MutableList`/`MutableMap`。迭代器链会让你感觉*非常*熟悉:
```
// Kotlin
val evens = (1..10).filter { it % 2 == 0 }.map { it * it }
// Rust — lazy iterators, terminated by a "collect"/consume
let evens: Vec = (1..=10)
.filter(|x| x % 2 == 0)
.map(|x| x * x)
.collect();
```
| Kotlin | Rust |
|---|---|
| `.map { }` | `.map(|x| ...)` |
| `.filter { }` | `.filter(|x| ...)` |
| `.forEach { }` | `.for_each(...)` 或 `for` 循环 |
| `.fold(0) { acc, x -> }` | `.fold(0, |acc, x| ...)` |
| `.sumOf { }` | `.map(...).sum()` |
| `.firstOrNull { }` | `.find(...)` → 返回 `Option` |
| `.groupBy { }` | 手动配合 `HashMap`,或使用 `itertools` crate |
| `.sortedBy { }` | `.sort_by_key(...)`(原地操作,作用于 `Vec`) |
迭代器是**惰性**的 —— 在调用消费方法(`collect`、`sum`、`for`、`count`)之前什么都不会执行。`.iter()` 进行借用,`.into_iter()` 进行消耗,`.iter_mut()` 提供可变引用。
## 17. 模块、可见性与包
```
// A "crate" = a package (a lib or binary). Cargo.toml is your build.gradle.
mod network { // module, like a Kotlin package/file boundary
pub fn connect() {} // `pub` = public; private by default
mod internal { } // nested, private
}
use network::connect; // like Kotlin import
connect();
```
| Kotlin | Rust |
|---|---|
| `internal` / private(默认是 public!) | **默认 private**,用 `pub` 暴露 |
| package | `mod`(模块)+ crate |
| Gradle 模块 / artifact | crate |
| `build.gradle`、Maven Central | `Cargo.toml`、[crates.io](https://crates.io) |
| `import foo.Bar` | `use foo::Bar;` |
注意极性的翻转:Kotlin 默认公开,而 Rust **默认私有**。
## 18. Async —— `suspend` → `async`/`.await`
形式类似于协程,但 Rust 不附带 runtime —— 你需要自己添加(几乎总是用 **`tokio`**)。
```
// Kotlin
suspend fun fetch(): String { ... }
val data = fetch()
// Rust
async fn fetch() -> String { ... }
let data = fetch().await; // .await is a postfix operator
```
```
#[tokio::main] // sets up the executor, like a coroutine scope
async fn main() {
let (a, b) = tokio::join!(fetch_a(), fetch_b()); // ~ awaitAll / coroutineScope
}
```
与协程的主要区别:
- `async fn` 返回一个**惰性的** `Future` —— 在被 `.await` 或 spawn 之前什么都不会做(Kotlin 协程一旦启动就是立即执行的)。
- 没有内置的 `Dispatchers` / 结构化并发 —— runtime(tokio)提供了 `spawn`、`join!`、`select!`、channel。
- `.await` 是后缀形式(`x.await`),并且能干净地进行链式调用。
## 19. 并发与 `Send`/`Sync` 保证
Kotlin 依靠你自己来避免数据竞争。Rust 通过同样的所有权规则加上两个 marker trait,将它们变成了**编译期错误**:
- `Send` —— 可以安全地移动到另一个线程。
- `Sync` —— 可以安全地在线程间共享(`&T`)。
```
use std::thread;
use std::sync::{Arc, Mutex};
let counter = Arc::new(Mutex::new(0)); // shared, thread-safe ownership
let mut handles = vec![];
for _ in 0..10 {
let c = Arc::clone(&counter);
handles.push(thread::spawn(move || { // `move` transfers ownership into the thread
*c.lock().unwrap() += 1;
}));
}
for h in handles { h.join().unwrap(); }
```
著名的口号**“无畏并发”**:只要它能编译通过,就没有数据竞争。这是借用检查器带来的回报。
## 20. 工具速成课
| 任务 | Kotlin/Gradle | Rust/Cargo |
|---|---|---|
| 新建项目 | IDE / `gradle init` | `cargo new my_app` |
| 构建 | `./gradlew build` | `cargo build`(`--release` 用于优化构建) |
| 运行 | `./gradlew run` | `cargo run` |
| 测试 | `./gradlew test` | `cargo test` |
| 添加依赖 | 编辑 `build.gradle` | `cargo add serde`(会自动编辑 `Cargo.toml`) |
| 格式化 | ktlint / spotless | `cargo fmt`(rustfmt —— 唯一的规范风格) |
| Lint | detekt | `cargo clippy` —— 确实非常棒,请经常运行它 |
| 文档 | Dokka | `cargo doc --open` |
| REPL | Kotlin REPL | 没有官方版本;使用 [play.rust-lang.org](https://play.rust-lang.org) |
`clippy` 是最好的学习工具 —— 它会建议惯用的重写方式。通过 **`rustup`** 安装一切(它是工具链管理器,类似于 Rust 版的 sdkman)。
在同一个文件中编写内联测试:
```
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_adds() { assert_eq!(add(2, 2), 4); }
}
```
## 21. 惯用法翻译备忘单
| Kotlin | Rust |
|---|---|
| `val x = 5` | `let x = 5;` |
| `var x = 5` | `let mut x = 5;` |
| `x?.foo() ?: default` | `x.map(\|v\| v.foo()).unwrap_or(default)` |
| `x!!` | `x.unwrap()` / `x.expect("msg")` |
| `x ?: return` | `let Some(x) = x else { return; };` |
| `if (x != null) { ... }` | `if let Some(x) = x { ... }` |
| `when (x) { ... }` | `match x { ... }` |
| `data class` | `#[derive(Clone, Debug, PartialEq)] struct` |
| `sealed class` | `enum`(变体携带数据) |
| `object`(单例) | 模块级函数,或 `static`,或 `Lazy` |
| `companion object` | 带有关联函数的 `impl` 块(`Type::new`) |
| `interface` | `trait` |
| `class Foo : Bar()`(继承) | 组合 + `trait`(无继承) |
| extension fun | `impl SomeTrait for Type` |
| `lazy { }` | `std::sync::LazyLock`(自 1.80 起稳定)/ `once_cell::sync::Lazy` |
| `require()` / `check()` | `assert!` / `debug_assert!` / 返回 `Err` |
| `throw` | `return Err(...)`(可恢复)或 `panic!`(Bug) |
| `try/catch` | 对 `Result` 使用 `match`,或使用 `?` 进行传播 |
| `List`(只读) | `&[T]`(切片) |
| `MutableList` | `Vec` |
| `Map` | `HashMap` |
| `to`/`Pair` | 元组 `(a, b)` |
| `.let { }` | `let` 绑定,或在 Option 上使用 `.map()` |
| `.also { }` | `{ let _ = &x; ... x }` / `inspect`(迭代器) |
| `.apply { }` | 直接构建 struct(字段是显式的) |
| `buildString { }` | `let mut s = String::new(); s.push_str(...);` |
## 22. Rust 有而 Kotlin 没有的东西
- **所有权与借用检查器** —— 在*没有垃圾回收器*且没有运行时开销的情况下,保证了内存和数据竞争安全。
- **真正的零开销抽象** —— 迭代器、泛型和 `async` 编译后都能达到手写代码的速度(单态化,没有类型擦除)。
- **以携带数据的 enum 作为默认建模工具** —— 比密封层级结构更轻量,且支持穷尽式匹配。
- **`?` 同时用于 `Option` 和 `Result`** —— 统一且显式的错误传播。
- **强大的模式匹配** —— 守卫、范围、绑定、嵌套解构、`let`-`else`。
- **卫生宏**(`println!`、`vec!`、`derive`) —— 无需注解处理器/KAPT 即可生成代码。
- **内置的唯一规范格式化工具和世界级的 linter**(`rustfmt`、`clippy`)。
- **编译为单一的自包含原生二进制文件** —— 无需 JVM,内存占用极小,非常适合 CLI、WASM 和嵌入式开发(使用 `musl` target 可实现完全静态链接)。
## 23. 你会怀念的 Kotlin 特性
- **GC。** 完全不需要思考所有权。在 Rust 中,构建图/双向链表是一堂*课程*,而不是一行代码的事。
- **`data class` 一行代码搞定**和 `copy()` —— Rust 需要使用派生宏和 struct 更新语法。
- **继承与 `open`/`override`** —— 你需要围绕组合和 trait 重新设计结构。
- **作用域函数**(`let`/`run`/`apply`/`also`/`with`) —— 没有直接的等价物;你需要编写普通代码。
- **毫不费力的共享可变状态** —— 每一个 `Rc>` 都在提醒你这并不是免费的。
- **快速的增量编译** —— Rust 的构建速度较慢;使用 `cargo check` 并保持 crate 较小会有所帮助。
- **`null` + `?.` 的简洁性** —— `Option` 更安全,但在熟悉这些组合子方法之前会显得比较啰嗦。
- **开箱即用的包含 Web/JSON 功能的标准库** —— Rust 依赖于 crate(`serde`、`reqwest`、`tokio`),这符合惯用法,但需要更多的手动组装。
## 24. 7 天学习计划
**第 1 天 —— 语法与绑定。** 安装 `rustup`,运行 `cargo new`。学习 `let`/`mut`、函数、`if`/`loop`/`for`、表达式与语句的区别(分号规则)。做 [Rustlings](https://github.com/rust-lang/rustlings) 的 `variables`、`functions`、`if`。
**第 2 天 —— 所有权。** 最重要的一天。阅读 [The Book 第 4 章](https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html)。理解 move vs copy vs clone,然后是借用(`&`、`&mut`)以及别名异或可变性规则。完成 Rustlings 的 `move_semantics`。
**第 3 天 —— Struct、enum、`match`。** 数据建模。`impl` 方法、`Option`、穷尽的 `match`、`if let`/`let else`。这时候你的 Kotlin `sealed class`/`when` 直觉就能派上用场了。完成 Rustlings 的 `structs`、`enums`、`options`。
**第 4 天 —— 错误处理与 trait。** `Result`、`?` 运算符、`panic!` 与可恢复错误的对比。然后是 `trait`:默认方法、`impl Trait for Type`、派生宏。完成 Rustlings 的 `error_handling`、`traits`。
**第 5 天 —— 泛型、集合、迭代器。** `Vec`、`HashMap` 以及迭代器链(`map`/`filter`/`collect`/`fold`)。带 trait 约束的泛型函数。完成 Rustlings 的 `generics`、`iterators`、`hashmaps`。
**第 6 天 —— 模块、工具实战、小项目。** `mod`/`use`/`pub`、`Cargo.toml`、`cargo add`。构建一个小型 CLI(待办事项列表或字数统计工具)。运行 `cargo clippy` 并阅读每一条建议。
**第 7 天 —— Async 或实战 crate。** 要么结合 `tokio` 学习 `async`/`.await`(使用 `reqwest` 调用 API,使用 `serde` 解析 JSON),要么通过 `Rc`/`Arc`/`RefCell` 深化对所有权的理解。阅读其他人编写的惯用代码。
**收藏这些:**
- [The Rust Book](https://doc.rust-lang.org/book/) —— 权威且优秀的教程
- [Rustlings](https://github.com/rust-lang/rustlings) —— 动手实践、编译驱动的练习
- [Rust by Example](https://doc.rust-lang.org/rust-by-example/) —— 可运行的代码片段
- [The Rust Playground](https://play.rust-lang.org) —— 即时尝试
- [Comprehensive Rust (Google)](https://google.github.io/comprehensive-rust/) —— 课程形式,非常适合有经验的开发者
- [std 文档](https://doc.rust-lang.org/std/) —— 可搜索,永远是事实的唯一来源
*归根结底:语法只需一个周末就能掌握。真正的核心课程是所有权 —— 把第 2 天花在那里,并在第 5 天重新阅读。一旦你不再与借用检查器对抗,而是开始听懂它说“你正在给可变状态创建别名,我不会让这变成一个 bug”,Rust 就会变成那种在编译期而不是凌晨 3 点的生产环境中捕获你错误的语言。多依赖 `clippy`,用 enum 构建一切模型,并在学习过程中毫无愧疚地使用 `.clone()` —— 以后再去优化那些借用也不迟。*标签:Kotlin, Rust, 可视化界面, 开发文档, 编程指南, 网络流量审计, 语言对比, 通知系统