AndroidPoet/rust-for-kotlin-devs

GitHub: AndroidPoet/rust-for-kotlin-devs

一份面向 Kotlin 开发者的 Rust 完整入门指南,通过代码对照和可运行示例帮助快速掌握 Rust 核心概念。

Stars: 48 | Forks: 1

Rust for Kotlin Developers

# Rust for Kotlin 开发者 —— 完整指南

Docs site Rust For Kotlin devs MIT License Runnable examples

📖 作为可搜索的文档站点阅读 →

**可运行的配套代码:** 每个概念在 [`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, 可视化界面, 开发文档, 编程指南, 网络流量审计, 语言对比, 通知系统