想踏入 Rust 的世界?太棒了!Rust 是一门令人兴奋的语言,它在性能、安全性和并发性方面都有着出色的表现。不过,就像任何新事物一样,刚开始可能会觉得有点“劝退”,但别担心,只要找对方法,你会发现 Rust 的魅力所在。
这篇文章就想跟你聊聊,如何一步一步、有条不紊地开启你的 Rust 之旅,让你从一个完全的门外汉,变成一个能够驾驭它的开发者。我会尽量讲得细致点,希望你能从中找到适合自己的节奏。
0. 心态调整:准备好迎接挑战与惊喜
在正式开始之前,先跟你打个招呼:Rust 的学习曲线确实比一些动态语言要陡峭一些。它强调的是“内存安全”和“无畏并发”,这两点是通过一些独特的设计和概念来实现的,比如所有权(Ownership)、借用(Borrowing)和生命周期(Lifetimes)。
拥抱错误信息: 你会发现 Rust 的编译器(叫做 `rustc`)非常“啰嗦”,会给你提供非常详细的错误信息,甚至告诉你怎么修改。一开始可能会觉得烦,但请记住,这是你的“好朋友”,它是在帮你写出更健壮的代码。学会读懂它,就是你迈出的重要一步。
耐心与毅力: 遇到搞不懂的概念,不要灰心。花点时间去理解,去尝试,去搜索。很多时候,当你感觉卡住了,换个角度思考,或者看看别人的代码,就会豁然开朗。
社区的力量: Rust 有一个非常活跃和友好的社区。遇到问题,别忘了去官方论坛、Discord 或者 Stack Overflow 求助。大家都很乐意帮助新人。
1. 工具的准备:让你的开发环境就绪
在你开始写代码之前,需要先把工具安装好。这部分非常简单,但却是基础。
1.1 安装 Rust 工具链 (rustup)
Rust 官方推荐使用 `rustup` 来管理 Rust 的版本和工具链。它不仅可以安装 Rust 编译器 (`rustc`),还有包管理器 (`cargo`)、文档生成器 (`rustdoc`) 等一系列开发必备工具。
访问官网: 打开 Rust 的官方网站:[https://www.rustlang.org/](https://www.rustlang.org/)
下载安装脚本: 网站会根据你的操作系统提供相应的安装指南。通常,你只需要在终端执行一行命令即可。
Linux/macOS:
```bash
curl proto '=https' tlsv1.2 sSf https://sh.rustup.rs | sh
```
脚本会提示你选择安装选项,通常直接按回车使用默认设置就好。
Windows:
访问 [https://rustup.rs/](https://rustup.rs/) 下载 `rustupinit.exe` 文件,然后双击运行即可。
验证安装: 安装完成后,重启你的终端(或者打开一个新的终端窗口),然后输入以下命令来验证安装是否成功:
```bash
rustc version
cargo version
```
如果能看到 Rust 编译器和 Cargo 的版本号,那就说明安装成功了!
1.2 选择一个合适的代码编辑器或 IDE
虽然 Rust 代码可以用任何文本编辑器编写,但为了获得更好的开发体验(语法高亮、自动补全、错误检查等),强烈建议使用一个支持 Rust 的编辑器或 IDE。
Visual Studio Code (VS Code): 这是目前最流行且功能强大的选择。安装好 VS Code 后,你只需要在扩展商店搜索并安装 `rustanalyzer` 这个插件。它提供了非常优秀的 Rust 代码智能提示和诊断功能。
IntelliJ IDEA (with Rust plugin): 如果你习惯 JetBrains 的产品线,那么 IntelliJ IDEA 配上官方的 Rust 插件也是一个不错的选择,提供了深度集成和强大的代码分析能力。
Neovim/Vim: 对于 Vim 用户来说,通过配置 Language Server Protocol (LSP) 客户端(如 `coc.nvim` 或内置的 `nvimlspconfig`)并使用 `rustanalyzer`,也能获得非常顺畅的 Rust 开发体验。
2. 入门第一步:Hello, World!
万事开头难,但 Rust 的“Hello, World!”非常简单。
2.1 创建一个新项目
Rust 的包管理器 `cargo` 是管理项目的基础。用 `cargo` 创建一个新项目非常方便。
打开你的终端,进入你想要存放项目的目录,然后执行:
```bash
cargo new hello_rust
```
这会创建一个名为 `hello_rust` 的新文件夹,里面包含了项目所需的基本结构:
```
hello_rust/
├── Cargo.toml
└── src/
└── main.rs
```
`Cargo.toml`: 这是项目的配置文件,记录了项目的名称、版本、作者以及依赖项等信息。
`src/main.rs`: 这是项目的源代码文件,`main` 函数是程序的入口。
2.2 编写你的第一个 Rust 程序
用你的代码编辑器打开 `hello_rust/src/main.rs` 文件。你会看到里面已经有了一段默认的代码:
```rust
fn main() {
println!("Hello, world!");
}
```
`fn main()`: 定义了一个名为 `main` 的函数,这是程序的入口点。Rust 程序总是从 `main` 函数开始执行。
`println!`: 这是一个宏(宏在 Rust 中用感叹号 `!` 表示),用于将文本输出到控制台。它会自动在输出的字符串末尾添加一个换行符。
`"Hello, world!"`: 这是要打印的字符串字面量。
`;`: Rust 中的大多数语句都以分号结尾。
2.3 编译和运行
回到终端,进入 `hello_rust` 文件夹,然后使用 `cargo` 来编译和运行你的程序。
编译:
```bash
cargo build
```
这会在项目的 `target/debug/` 目录下生成一个可执行文件。`cargo build` 只进行编译,不运行程序。
运行:
```bash
cargo run
```
`cargo run` 会先编译你的项目(如果需要的话),然后直接运行生成的可执行文件。你会在终端看到输出:
```
Hello, world!
```
构建发布版本:
```bash
cargo build release
```
这会进行更优化的编译,生成速度更快但编译时间稍长的可执行文件,通常用于部署。发布版本的可执行文件在 `target/release/` 目录下。
恭喜你!你已经成功运行了你的第一个 Rust 程序。这仅仅是开始,但意义非凡。
3. 学习核心概念:Rust 的基石
现在,是时候深入了解 Rust 的核心概念了。我会按照一般学习的顺序来介绍,并给出一些思考点。
3.1 变量与可变性 (Variables and Mutability)
Rust 的变量默认是不可变的。这意味着一旦给变量赋值,就不能再改变它的值。如果你想改变一个变量的值,需要使用 `mut` 关键字来声明它是可变的。
```rust
fn main() {
let x = 5; // 默认不可变
// x = 6; // 这行代码会报错,因为 x 是不可变的
let mut y = 5; // 声明 y 是可变的
println!("The value of y is: {}", y);
y = 6; // 这行代码是合法的
println!("The value of y is now: {}", y);
// 常量与变量的区别
const MAX_POINTS: u32 = 100_000; // 常量,必须指定类型,且总是不可变的
println!("The maximum points allowed is: {}", MAX_POINTS);
}
```
思考: 为什么 Rust 会默认不可变?思考一下这带来的好处(例如,在并发编程中更容易推理,减少意外的副作用)。
3.2 数据类型 (Data Types)
Rust 是一种静态类型语言,这意味着它在编译时就知道所有变量的类型。Rust 提供了丰富的数据类型。
标量类型 (Scalar Types):
整数 (Integers): 有符号和无符号,不同大小(`i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`)。`usize` 的大小与你的计算机的指针大小相同。
浮点数 (FloatingPoint Numbers): `f32` 和 `f64`。
布尔值 (Booleans): `bool`,值为 `true` 或 `false`。
字符 (Characters): `char`,代表一个 Unicode 字符。
复合类型 (Compound Types):
元组 (Tuples): 固定长度的异构序列。
```rust
let tup: (i32, f64, u8) = (500, 6.4, 1);
let (x, y, z) = tup; // 解构元组
println!("The value of y is: {}", y);
println!("The first element is: {}", tup.0); // 通过索引访问
```
数组 (Arrays): 固定长度的同构序列。
```rust
let a = [1, 2, 3, 4, 5];
let b: [i32; 5] = [1, 2, 3, 4, 5]; // 指定类型和长度
let first = a[0];
let second = a[1];
// let index = 10;
// let element = a[index]; // 这行会因数组越界而在运行时 panic
```
思考: 数组越界为什么会导致 `panic`?这是 Rust 安全性的体现之一。
3.3 函数 (Functions)
函数是代码块,用于执行特定任务。
```rust
fn main() {
another_function(5, 'h');
let x = five();
println!("The value of x is: {}", x);
let y = add_one(5);
println!("The value of y is: {}", y);
}
fn another_function(x: i32, unit_label: char) {
println!("The measurement is: {}{}", x, unit_label);
}
fn five() > i32 {
5 // 表达式作为返回值,不需要分号
}
fn add_one(x: i32) > i32 {
x + 1 // 表达式作为返回值
}
```
返回值: 函数的返回值是函数体中最后一个表达式的值(没有分号),或者使用 `return` 关键字显式返回。
3.4 控制流 (Control Flow)
包括条件语句和循环。
`if` 表达式:
```rust
fn main() {
let number = 3;
if number < 5 {
println!("condition was true");
} else {
println!("condition was false");
}
// if 也是一个表达式,可以用于赋值
let condition = true;
let x = if condition { 5 } else { 6 };
println!("The value of x is: {}", x);
}
```
注意: `if` 的每个分支都必须返回相同类型的值。
`loop` 循环: 无限循环,直到遇到 `break`。
```rust
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter 2; // break 也可以返回值
}
};
println!("The result is: {}", result);
}
```
`while` 循环: 条件循环。
```rust
fn main() {
let mut number = 3;
while number != 0 {
println!("{}!", number);
number = 1;
}
println!("LIFTOFF!!!");
}
```
`for` 循环: 迭代序列。
```rust
fn main() {
let a = [10, 20, 30, 40, 50];
for element in a {
println!("the value is: {}", element);
}
// 迭代范围
for number in (1..4).rev() { // 1, 2, 3
println!("{}!", number);
}
println!("LIFTOFF!!!");
}
```
`1..4` 表示一个包含 1,不包含 4 的范围 (1, 2, 3)。
`.rev()` 将范围反转。
3.5 所有权 (Ownership)
这是 Rust 最核心,也可能是最难理解的部分。它解决了垃圾回收(GC)和手动内存管理(如 C/C++)的痛点,在保证内存安全的同时,也带来了极高的性能。
所有权规则:
1. Rust 中的每一个值都有一个被这个值的变量所拥有的所有者。
2. 一次只能有一个所有者。
3. 当所有者离开作用域(离开 `{}` 块),这个值会被丢弃(drop)。
理解所有权需要关注以下几个概念:
Move(移动): 当你将一个值从一个变量赋给另一个变量时,所有权会发生转移(move)。
```rust
fn main() {
let s1 = String::from("hello");
let s2 = s1; // s1 的所有权被移交给 s2
// println!("{}, world!", s1); // 这行会报错,s1 已经失效了
println!("{}, world!", s2);
}
```
为什么会发生 Move? 对于存储在堆上的数据(如 `String`),Rust 为了避免一次性复制整个堆上的数据(性能开销大),而是通过转移一个指针、长度和容量的“头部信息”来完成赋值。当旧变量(如 `s1`)离开作用域时,Rust 会调用 `drop` 函数来释放内存,但如果允许旧变量继续有效,可能会导致双重释放(double free)的问题。所以 Rust 禁止了这种行为,强制进行 Move。
Clone(克隆): 如果你希望在不转移所有权的情况下复制堆上的数据,可以使用 `.clone()` 方法。这会进行深拷贝,复制堆上的所有数据。
```rust
fn main() {
let s1 = String::from("hello");
let s2 = s1.clone(); // s2 拥有 s1 的一个独立副本
println!("s1 = {}, s2 = {}", s1, s2); // 两者都有效
}
```
Copy Trait: 像整数、浮点数、布尔值、字符以及包含这些类型的元组和数组等存储在栈上的数据,它们的大小在编译时是固定的。当你将这些值赋给另一个变量时,Rust 会进行Copy操作,即简单地复制值,而不是 Move。原始变量仍然有效。
```rust
fn main() {
let x = 5;
let y = x; // 这是一个 Copy 操作,x 仍然有效
println!("x = {}, y = {}", x, y);
}
```
重要: 如果一个类型实现了 `Drop` trait,那么它就不能自动实现 `Copy` trait。`String` 类型就实现了 `Drop` trait(因为需要手动管理内存),所以它不是 `Copy` 的。
函数传参和返回值中的所有权: 函数传参和返回值也会发生所有权的转移。
```rust
fn main() {
let s = String::from("hello"); // s 进入作用域
takes_ownership(s); // s 的值移动进函数,因此在下面 s 不再有效
// println!("{}", s); // 报错
let x = 5; // x 进入作用域
makes_copy(x); // x 的值被拷贝进函数
println!("{}", x); // x 仍然有效
}
fn takes_ownership(some_string: String) { // some_string 进入作用域
println!("{}", some_string);
} // some_string 离开作用域,drop 被调用,内存被释放
fn makes_copy(some_integer: i32) { // some_integer 进入作用域
println!("{}", some_integer);
} // some_integer 离开作用域,但它是 Copy 的,所以不会被释放,这 nothing
```
3.6 引用与借用 (References and Borrowing)
所有权转移在函数传参时很不方便,如果我们只需要读取一个值,而不获取它的所有权呢?这时候就需要引用(Reference)和借用(Borrowing)了。
创建引用: 使用 `&` 符号创建一个引用。引用就像一个指针,但它保证总是指向一个有效的对象。
```rust
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1); // 传递 s1 的引用
println!("The length of '{}' is {}.", s1, len); // s1 仍然有效
}
fn calculate_length(s: &String) > usize {
s.len()
}
```
`&s1` 是一个不可变引用。
可变引用: 如果需要修改被引用的值,需要使用可变引用 `&mut`。
```rust
fn main() {
let mut s = String::from("hello");
change(&mut s);
println!("{}", s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
```
借用规则: 这是理解所有权和引用的关键。
1. 在任何给定时间,要么只能有一个可变引用,要么只能有任意数量的不可变引用。
2. 引用必须总是有效的。
为什么要有这些规则? 这是为了防止数据竞争(data race)。数据竞争发生在至少满足以下三个条件时:
有两个或更多指针同时访问同一块数据。
至少有一个指针被用于写入数据。
没有使用任何机制来同步访问。
示例:
```rust
fn main() {
let mut s = String::from("hello");
// let r1 = &s // 正常,不可变引用
// let r2 = &s // 正常,多个不可变引用
// println!("{}, {}", r1, r2);
// let r3 = &mut s; // 错误!不能在有不可变引用的情况下创建可变引用
// println!("{}", r3);
let r1 = &s
println!("The length of '{}' is {}.", s, r1.len());
// println!("The length of '{}' is {}.", s, r1.len()); // 这里 r1 还在使用,所以不能创建可变引用
let r2 = &mut s; // OK!r1 不再使用,所以可以创建可变引用
println!("{}", r2);
// let r3 = &mut s; // 错误!不能有两个可变引用
// println!("{}, {}", r2, r3);
}
```
这些规则让 Rust 在编译时就能发现潜在的数据竞争问题。
3.7 切片 (Slices)
切片是一种允许你引用一个集合中连续元素序列的类型,而无需获取整个集合的所有权。
字符串切片:
```rust
fn main() {
let mut s = String::from("hello world");
let hello = &s[0..5]; // 引用前 5 个字节
let world = &s[6..11]; // 引用从第 6 个字节到第 11 个字节
let slice = &s[..]; // 引用整个字符串
println!("{}, {}", hello, world);
// 注意:如果字符串中包含多字节字符,直接使用字节索引可能会导致 panic
// 例如,如果 s 是 "你好世界",s[0..1] 只会得到第一个字节,而不是第一个字符
// 通常使用字符串切片时,要确保切片发生在 UTF8 字符边界上。
// 创建一个函数接收字符串切片
let word = first_word(&s);
// s.clear(); // 错误!不能修改 s,因为 hello 是对 s 的不可变引用
println!("The first word is: {}", word);
}
fn first_word(s: &str) > &str { // &str 是一个字符串切片类型
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
```
`&str` 类型是字符串切片,它是一个“胖指针”,包含指向字符串数据的指针和字符串的长度。
4. 深入学习 Rust 生态
掌握了基础之后,接下来就是学习 Rust 的强大生态系统了。
4.1 包和模块系统 (Packages, Crates, and Modules)
Crate (箱): Rust 的编译单元,可以是库(library)或二进制文件(binary)。`cargo new` 创建的项目就是一个 crate。
Package (包): 一个或多个 crate 的集合,必须包含一个 `Cargo.toml` 文件。通常一个包只包含一个库 crate 或一个二进制 crate,但也可以包含多个。
Modules (模块): Rust 使用模块来组织代码,控制代码的可见性(public/private)。可以使用 `mod` 关键字来定义模块,`pub` 关键字来暴露内容。
```rust
// src/main.rs
mod front_of_house {
pub mod hosting { // front_of_house::hosting 是公有的
pub fn add_to_waitlist() {}
}
}
// 可以使用 `use` 关键字引入模块或项
use crate::front_of_house::hosting;
fn main() {
hosting::add_to_waitlist();
}
```
4.2 Cargo 的更多用法
Cargo 是 Rust 的瑞士军刀,除了编译和运行,还有很多常用功能:
添加依赖: 在 `Cargo.toml` 的 `[dependencies]` 部分添加其他 crate 的名称和版本。
```toml
[dependencies]
rand = "0.8.5"
```
然后运行 `cargo build` 或 `cargo check`,Cargo 就会自动下载并编译这些依赖。
`cargo check`: 快速检查代码是否能通过编译,但不生成可执行文件,速度更快。
`cargo test`: 运行项目中的所有测试。
`cargo doc open`: 生成项目及其依赖的文档,并在浏览器中打开。
工作空间 (Workspaces): 用于管理多个相关联的包。
4.3 Rust 标准库 (The Standard Library)
Rust 的标准库提供了许多基础类型和功能,例如:
`String` 和 `&str`:字符串处理。
`Vec`:动态数组(向量)。
`HashMap`:哈希表。
`Option`:用于处理可能不存在的值(`Some(value)` 或 `None`),这是 Rust 安全性的重要体现,避免了空指针。
`Result`:用于表示操作可能成功(`Ok(value)`)或失败(`Err(error)`)。这是 Rust 处理错误的主要方式。
4.4 错误处理 (Error Handling)
Rust 对错误的分类和处理方式很独特:
Panic: 表示程序遇到无法恢复的错误,会导致程序立即终止并释放资源。通常用于不可预见的编程错误(如数组越界)。
`Result`: 用于表示可恢复的错误。通常使用 `match` 或 `?` 操作符来处理。
```rust
use std::fs::File;
use std::io::{self, Read};
fn read_username_from_file() > Result {
let mut f = File::open("hello.txt")?; // ? 操作符会自动处理 Result
let mut s = String::new();
f.read_to_string(&mut s)?;
Ok(s)
}
fn main() {
match read_username_from_file() {
Ok(username) => println!("Username: {}", username),
Err(e) => println!("Error reading username: {}", e),
}
}
```
`?` 操作符:当在一个返回 `Result` 的函数中使用 `?` 时,如果 `Result` 是 `Err`,它会立即返回 `Err`;如果是 `Ok`,它会解包出值并继续执行。这极大地简化了错误处理代码。
4.5 Trait 和泛型 (Traits and Generics)
泛型 (Generics): 允许你编写不依赖于特定类型的代码。
```rust
// 泛型函数
fn largest(list: &[T]) > T {
let mut largest = list[0];
for &item in list {
if item > largest {
largest = item;
}
}
largest
}
// 泛型结构体
struct Point {
x: T,
y: T,
}
```
Trait: 定义了共享行为的抽象。类似于其他语言的接口或抽象类。
```rust
pub trait Summary {
fn summarize(&self) > String;
}
// 实现 trait
impl Summary for NewsArticle {
fn summarize(&self) > String {
format!("{}, by {} ({})", self.headline, self.author, self.location)
}
}
// Trait bound:要求泛型参数实现某个 trait
fn notify(item: &T) {
println!("Breaking news! {}", item.summarize());
}
```
4.6 生命周期 (Lifetimes)
生命周期是 Rust 的另一个核心概念,它确保引用不会比它们指向的数据活得更长。它解决了悬垂引用(dangling reference)的问题。
生命周期注解: 在函数签名中,编译器需要知道引用之间的关系,以便进行静态分析。
```rust
// 下面的函数是无效的,因为返回的引用可能比传入的字符串短命
// fn longest_invalid(x: &str, y: &str) > &str {
// if x.len() > y.len() {
// x
// } else {
// y
// }
// }
// 正确的写法,生命周期注解 'a
fn longest<'a>(x: &'a str, y: &'a str) > &'a str {
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("abcd");
let string2 = "xyz";
let result = longest(string1.as_str(), string2);
println!("The longest string is {}", result);
}
```
`'a` 是一个生命周期注解,表示 `x` 和 `y` 必须至少活得和返回的那个引用的生命周期一样长。
思考: 生命周期注解本身不会改变引用能活多久,它只是帮助编译器检查和推理引用之间的关系。
5. 进阶之路:深入探索
当你对 Rust 的基础有了扎实的掌握后,可以开始探索更高级的领域:
并发编程: Rust 在并发方面表现出色,其所有权和借用机制消除了许多常见的并发错误(如数据竞争)。学习 `std::thread`、`Arc`(原子引用计数)、`Mutex`(互斥锁)等。
智能指针 (Smart Pointers): 如 `Box`(堆分配)、`Rc`(引用计数)、`RefCell`(运行时借用检查)等,它们提供了比普通引用更丰富的功能。
模式匹配 (Pattern Matching): Rust 的 `match` 语句非常强大,可以用来解构各种数据类型。
宏 (Macros): 学习如何编写声明宏 (`macro_rules!`) 和过程宏,扩展语言的功能。
不安全 Rust (Unsafe Rust): 在需要绕过 Rust 的一些安全检查时(例如与 C 代码交互、编写高性能系统库),可以使用 `unsafe` 块。但这应该谨慎使用,并且需要格外小心。
FFI (Foreign Function Interface): 如何在 Rust 中调用 C/C++ 代码,或将 Rust 代码暴露给其他语言调用。
异步编程 (Async/Await): Rust 的 `async`/`await` 语法提供了高效的异步操作,非常适合网络编程、IO 密集型任务。
6. 学习资源推荐
好的资源能让你事半功倍。
1. 《The Rust Programming Language》 (官方指南):
这是学习 Rust 的 必读 资源。它以易于理解的方式介绍了 Rust 的所有核心概念。
在线阅读:[https://doc.rustlang.org/book/](https://doc.rustlang.org/book/)
国内有汉化版本,搜索“Rust 程序设计语言”即可找到。
2. Rustlings:
这是一个非常棒的交互式练习项目,通过完成一系列小练习来学习 Rust 的语法和概念。
GitHub:[https://github.com/rustlang/rustlings](https://github.com/rustlang/rustlings)
3. Rust By Example:
提供大量可运行的示例代码,展示 Rust 的各种特性和用法。
在线阅读:[https://doc.rustlang.org/rustbyexample/](https://doc.rustlang.org/rustbyexample/)
4. Rust Cookbook:
当你需要解决具体问题时,可以查阅这个食谱,里面有很多实用的代码示例和模式。
在线阅读:[https://rustlangnursery.github.io/rustcookbook/](https://rustlangnursery.github.io/rustcookbook/)
5. Rust API 文档:
随时查阅标准库和第三方库的文档。
标准库:[https://doc.rustlang.org/std/](https://doc.rustlang.org/std/)
7. 实践!实践!再实践!
学习编程最重要的一点就是动手实践。
尝试修改示例代码: 在学习过程中,不要只看不练,尝试修改官方文档和 Rustlings 中的代码,看看会发生什么。
小项目: 尝试用 Rust 写一些小工具,例如:
一个简单的命令行工具(比如一个计算器、一个文件查找工具)。
一个简单的网络爬虫。
一个文本处理程序。
一个简单的游戏(可以使用 `ggez` 或 `bevy` 这样的游戏引擎库)。
参与开源项目: 当你对 Rust 有一定的了解后,可以尝试为开源项目贡献代码,这是提升技能、学习最佳实践的好方法。
结语
学习 Rust 的过程可能充满挑战,但当你克服了初期的困难,你将会发现一门如此强大、可靠且充满乐趣的语言。它会改变你对软件开发的看法,让你写出更安全、更高效的代码。
享受这个过程吧!不断提问,不断尝试,你终将成为一名熟练的 Rust 开发者。祝你旅途愉快!