19 四月 2025

Rust 教程:新手入门指南

Rust 持续吸引着开发者的兴趣,连续多年在 Stack Overflow 调查中获得“最受欢迎”编程语言的称号。这不仅仅是炒作;Rust 提供了一套引人注目的高性能、安全性和现代语言特性的组合,解决了其他系统编程语言中常见的痛点。如果你对 Rust 的独特之处感到好奇并想开始你的学习之旅,这份初学者指南将为你提供入门所需的基础知识。我们将探讨核心语法、像所有权这样的独特概念,以及支撑 Rust 生态系统的基本工具。

为何要学习 Rust 编程?

Rust 将自己定位为一种用于构建可靠且高效软件的语言。其主要优势在于无需依赖垃圾回收器即可实现内存安全,并能实现无畏并发。以下是你应该考虑学习 Rust 的原因:

  1. 性能: Rust 直接编译成本地机器码,提供与 C 和 C++ 相媲美的性能。它在实现这种速度的同时不牺牲安全性,使其适用于性能关键型应用,如游戏引擎、操作系统、浏览器组件和高性能 Web 服务。
  2. 内存安全: Rust 的旗舰特性是其所有权系统,辅以借用生命周期。该系统在编译时保证内存安全。告别那些经常困扰 C/C++ 等语言的悬垂指针、缓冲区溢出或数据竞争。Rust 编译器就像一个严格的守门员,在你的代码运行之前就阻止这些常见错误。
  3. 并发: 并发编程(让多个任务看似同时运行)是出了名的难以正确实现。Rust 直面这个问题。所有权和类型系统协同工作,在编译时防止数据竞争,使得编写多线程应用程序变得更加容易和安全。这种“无畏并发”让开发者能够有效利用现代多核处理器,而无需担心常见的陷阱。
  4. 现代工具: Rust 配备了 Cargo,这是一个集成在核心体验中的出色包管理器和构建工具。Cargo 无缝地处理依赖管理、测试、构建和发布 Crate(Rust 对包或库的称呼),简化了整个开发工作流程。
  5. 不断发展的生态系统和社区: Rust 社区以其充满活力、积极活跃且对新人友好而闻名。库(Crate)的生态系统正在迅速扩展,涵盖了从 Web 开发(如 Actix、Rocket、Axum 等框架)和网络(如用于异步操作的 Tokio)到嵌入式系统、数据科学和命令行工具等多个领域。

学习 Rust 编程:必备工具

在编写第一行 Rust 代码之前,你需要设置 Rust 工具链。安装 Rust 的标准且推荐的方法是使用 rustup,即 Rust 工具链安装器。

  1. rustup:这个命令行工具管理你的 Rust 安装。它允许你安装、更新以及轻松切换不同的 Rust 版本(如稳定版、测试版或每夜构建版)。访问 Rust 官方网站(https://www.rust-lang.org/tools/install)获取针对你操作系统的具体安装说明。
  2. rustc:这是 Rust 编译器。在你将 Rust 源代码写入 .rs 文件后,rustc 将它们编译成你的计算机可以理解的可执行二进制文件或库。虽然它至关重要,但在日常工作流程中,你通常不会直接频繁调用 rustc
  3. cargo:这是 Rust 的构建系统和包管理器,也是你将最常与之交互的工具。Cargo 负责协调许多常见的开发任务:
    • 创建新项目(cargo new)。
    • 构建你的项目(cargo build)。
    • 运行你的项目(cargo run)。
    • 运行自动化测试(cargo test)。
    • 管理依赖项(自动获取并编译 Cargo.toml 文件中列出的外部库或 Crate)。
    • 将你自己的 Crate 发布到 crates.io(Rust 的中央包注册中心)供他人使用。

如果想快速进行实验而无需本地安装,像官方的 Rust Playground 或集成开发环境如 Replit 这样的在线平台是非常好的选择。

你的第一个 Rust 程序:Hello, Rust!

让我们从传统的“Hello, world!”程序开始,这是学习任何新语言的必经之路。创建一个名为 main.rs 的文件,并添加以下代码:

fn main() {
    // 这行代码将文本打印到控制台
    println!("Hello, Rust!");
}

要使用基本工具编译并运行这个简单的程序:

  1. 编译: 打开你的终端或命令提示符,导航到包含 main.rs 的目录,然后运行:
    rustc main.rs
    
    这个命令调用 Rust 编译器(rustc),它会创建一个可执行文件(例如,在 Linux/macOS 上是 main,在 Windows 上是 main.exe)。
  2. 运行: 在你的终端中执行编译好的程序:
    ./main
    # 在 Windows 上,使用: .\main.exe
    

然而,对于任何超出单个文件的项目,使用 Cargo 是标准且方便得多的方法:

  1. 创建新的 Cargo 项目: 在你的终端中运行:
    cargo new hello_rust
    cd hello_rust
    
    Cargo 会创建一个名为 hello_rust 的新目录,其中包含一个 src 子目录(内有 main.rs,已填充“Hello, Rust!”代码)和一个名为 Cargo.toml 的配置文件。
  2. 使用 Cargo 运行: 只需运行:
    cargo run
    
    Cargo 将处理编译步骤,然后执行生成的程序,在你的控制台上显示“Hello, Rust!”。

让我们分解一下这段代码:

  • fn main(): 这定义了主函数。fn 关键字表示函数声明。main 是一个特殊的函数名;它是每个可执行 Rust 程序开始运行的入口点。括号 () 表示该函数不接受任何输入参数。
  • {}: 花括号定义了一个代码块或作用域。所有属于该函数的代码都放在这些花括号内。
  • println!("Hello, Rust!");: 这一行执行将文本打印到控制台的操作。
    • println! 是一个 Rust 。宏与函数类似,但有一个关键区别:它们以感叹号 ! 结尾。宏执行编译时代码生成,提供比常规函数更强大的功能和灵活性(例如处理可变数量的参数,println! 就是这样做的)。println! 宏将提供的文本打印到控制台,并自动在末尾添加一个换行符。
    • "Hello, Rust!" 是一个字符串字面量——表示文本的固定字符序列,用双引号括起来。
    • ;: 分号标记语句的结束。大多数可执行的 Rust 代码行(语句)都以分号结束。

Rust 初学者教程:关键概念

现在,让我们深入了解 Rust 编程语言的基本构建块。

变量与可变性

变量用于存储数据值。在 Rust 中,你使用 let 关键字声明变量。

let apples = 5;
let message = "Take five";

Rust 的一个核心概念是变量默认是不可变的。这意味着一旦一个值绑定到一个变量名上,你以后就不能更改该值。

let x = 10;
// x = 15; // 这一行会导致编译时错误!无法对不可变变量 `x` 进行二次赋值。
println!("x 的值是: {}", x); // {} 是 x 值的占位符

这种默认的不可变性是一种刻意的设计选择,它通过防止意外修改数据(这可能是错误的常见来源)来帮助你编写更安全、更可预测的代码。如果你确实需要一个值可以改变的变量,你必须在声明时使用 mut 关键字明确地将其标记为可变。

let mut count = 0; // 将 'count' 声明为可变
println!("初始计数: {}", count);
count = 1; // 这是允许的,因为 'count' 是用 'mut' 声明的
println!("新的计数: {}", count);

Rust 还允许遮蔽(Shadowing)。你可以在同一作用域内声明一个与先前变量同名的变量。新变量会“遮蔽”旧变量,意味着后续使用该名称将引用新变量。这与可变性不同,因为我们正在创建一个全新的变量,它甚至可以有不同的类型。

let spaces = "   "; // 'spaces' 最初是一个字符串切片 (&str)
let spaces = spaces.len(); // 'spaces' 现在被一个持有其长度(一个整数,usize)的新变量遮蔽了
println!("空格的数量: {}", spaces); // 打印整数值

基本数据类型

Rust 是一种静态类型语言。这意味着编译器必须在编译时知道每个变量的类型。然而,Rust 具有出色的类型推断能力。在许多情况下,你不需要显式地写出类型;编译器通常可以根据值以及你如何使用它来推断出类型。

let quantity = 10;         // 编译器推断为 i32(默认的有符号整数类型)
let price = 9.99;          // 编译器推断为 f64(默认的浮点数类型)
let active = true;         // 编译器推断为 bool(布尔值)
let initial = 'R';         // 编译器推断为 char(字符)

如果你想要或需要明确指定类型(例如,为了清晰起见或当编译器需要帮助时),你可以使用冒号 : 后跟类型名称来提供类型注解

let score: i32 = 100;       // 显式指定为有符号 32 位整数
let ratio: f32 = 0.5;       // 显式指定为单精度浮点数
let is_complete: bool = false; // 显式指定为布尔值
let grade: char = 'A';      // 显式指定为字符(Unicode 标量值)

Rust 有几种内置的标量类型(表示单个值):

  • 整数: 有符号整数(i8, i16, i32, i64, i128, isize)存储正整数、负整数和零。无符号整数(u8, u16, u32, u64, u128, usize)仅存储非负整数(包括零)。isizeusize 类型的大小取决于计算机的架构(32 位或 64 位),主要用于索引集合。
  • 浮点数: f32(单精度)和 f64(双精度)。默认是 f64
  • 布尔值: bool 类型有两个可能的值:truefalse
  • 字符: char 类型表示单个 Unicode 标量值(比仅 ASCII 字符更全面),用单引号括起来(例如,'z', 'π', '🚀')。

字符串:String vs &str

在 Rust 中处理文本通常涉及两种主要的类型,这可能会让新手感到困惑:

  1. &str(读作“字符串切片”):这是对存储在内存某处的 UTF-8 编码字节序列的不可变引用。字符串字面量(如 "Hello")的类型是 &'static str,意味着它们直接存储在程序的可执行文件中,并在程序的整个生命周期内存在。切片提供了对字符串数据的视图,但不拥有它。它们的大小是固定的。
  2. String:这是一个有所有权的可增长的可变的、UTF-8 编码的字符串类型。String 的数据存储在上,允许其调整大小。当你需要修改字符串数据,或者字符串需要拥有其数据并管理自己的生命周期时(通常在从函数返回字符串或将其存储在结构体中时),你会使用 String
// 字符串字面量(存储在程序二进制文件中,不可变)
let static_slice: &'static str = "我是不可变的";

// 从字面量创建一个有所有权的、堆分配的 String
let mut dynamic_string: String = String::from("开始");

// 修改 String(这是可能的,因为它是可变的并且拥有其数据)
dynamic_string.push_str("并增长");
println!("{}", dynamic_string); // 输出: 开始并增长

// 创建一个引用 String 部分内容的字符串切片
// 这个切片借用了 dynamic_string 的数据
let slice_from_string: &str = &dynamic_string[0..5]; // 引用 "开始"
println!("切片: {}", slice_from_string);

函数

函数是将代码组织成命名的、可重用单元的基础。我们已经遇到过特殊的 main 函数。

// 函数定义
fn greet(name: &str) { // 接受一个参数:'name',它是一个字符串切片 (&str)
    println!("你好, {}!", name);
}

// 接受两个 i32 参数并返回一个 i32 的函数
fn add(a: i32, b: i32) -> i32 {
    // 在 Rust 中,函数体中的最后一个表达式会自动作为返回值,
    // 只要它不以分号结尾。
    a + b
    // 这等同于写:return a + b;
}

fn main() {
    greet("爱丽丝"); // 调用 'greet' 函数

    let sum = add(5, 3); // 调用 'add',将返回的值绑定到 'sum'
    println!("5 + 3 = {}", sum); // 输出: 5 + 3 = 8
}

关于函数的要点:

  • 使用 fn 关键字声明函数。
  • 在括号内指定参数名称及其类型。
  • 在箭头 -> 之后指定函数的返回类型。如果函数不返回值,其返回类型隐式为 ()(一个空元组,通常称为“单元类型”)。
  • 函数体由一系列语句(执行操作的指令,通常以 ; 结尾)组成,并可选地以一个表达式(计算出值的某物)结尾。
  • 如果函数块中的最后一个表达式没有分号,它的值将自动返回。return 关键字可用于从函数内部的任何位置进行显式的、提前的返回。

控制流

Rust 提供了标准的控制流结构来决定代码执行的顺序:

  • if/else/else if 用于条件执行。
let number = 6;

if number % 4 == 0 {
    println!("数字可以被 4 整除");
} else if number % 3 == 0 {
    println!("数字可以被 3 整除"); // 这个分支会执行
} else {
    println!("数字不能被 4 或 3 整除");
}

// 重要:在 Rust 中,'if' 是一个表达式,意味着它会计算出一个值。
// 这允许你直接在 'let' 语句中使用它。
let condition = true;
let value = if condition { 5 } else { 6 }; // value 将是 5
println!("这个值是: {}", value);
// 注意:'if' 表达式的两个分支必须计算出相同类型的值。
  • 循环: 用于重复执行。
    • loop:创建一个无限循环。你通常使用 break 来退出循环,可以选择性地返回一个值。
    • while:只要指定的条件保持为真就一直循环。
    • for:遍历集合或范围中的元素。这是 Rust 中最常用且通常最安全的循环类型。
// loop 示例
let mut counter = 0;
let result = loop {
    counter += 1;
    if counter == 10 {
        break counter * 2; // 退出循环并返回 'counter * 2'
    }
};
println!("循环结果: {}", result); // 输出: 循环结果: 20

// while 示例
let mut num = 3;
while num != 0 {
    println!("{}!", num);
    num -= 1;
}
println!("起飞!!!");

// for 示例(遍历数组)
let a = [10, 20, 30, 40, 50];
for element in a.iter() { // .iter() 创建一个数组元素的迭代器
    println!("值是: {}", element);
}

// for 示例(遍历范围)
// (1..4) 创建一个包含 1, 2, 3 的范围(不包括 4)
// .rev() 反转迭代器
for number in (1..4).rev() {
    println!("{}!", number); // 打印 3!, 2!, 1!
}
println!("再次起飞!!!");

注释

使用注释向你的代码添加编译器会忽略的解释和说明。

// 这是单行注释。它延伸到行尾。

/*
 * 这是多行块注释。
 * 它可以跨越多行,对于
 * 较长的解释很有用。
 */

 let lucky_number = 7; // 你也可以在行尾放置注释。

理解所有权:Rust 的核心概念

所有权是 Rust 最独特和核心的特性。正是这个机制使得 Rust 能够在编译时保证内存安全,而无需垃圾回收器。掌握所有权是理解 Rust 的关键。它遵循三个核心规则:

  1. 所有者: Rust 中的每个值都有一个变量被指定为其所有者
  2. 唯一所有者: 在任何给定时间,一个特定的值只能有一个所有者。
  3. 作用域与丢弃: 当所有者变量离开作用域(例如,它在其内声明的函数结束时),它所拥有的值将被丢弃。这意味着它的内存会自动被释放。
{ // s 在这里无效,它尚未声明
    let s = String::from("hello"); // 从此点开始 s 有效;
                                    // s '拥有' 在堆上分配的 String 数据。
    // 你可以在这里使用 s
    println!("{}", s);
} // 作用域到此结束。's' 不再有效。
  // Rust 自动为 s 所拥有的 String 调用一个特殊的 'drop' 函数,
  // 释放其堆内存。

当你将一个拥有所有权的值(如 StringVec 或包含拥有所有权类型的结构体)赋给另一个变量,或者按值将其传递给函数时,所有权会移动。原始变量变得无效。

let s1 = String::from("原始值");
let s2 = s1; // String 数据的所有权从 s1 移动到了 s2。
             // 此后 s1 不再被视为有效。

// println!("s1 是: {}", s1); // 编译时错误!值在移动后被借用。
                              // s1 不再拥有数据。
println!("s2 是: {}", s2); // s2 现在是所有者并且有效。

这种移动行为防止了“二次释放”错误,即两个变量可能在离开作用域时意外地尝试释放相同的内存位置。像整数、浮点数、布尔值和字符这样的基本类型实现了 Copy Trait,这意味着它们在赋值或传递给函数时会被简单地复制而不是移动。

借用与引用

如果你想让一个函数使用某个值不转移所有权怎么办?你可以创建引用。创建引用被称为借用。引用允许你访问由另一个变量拥有的数据,而无需获得其所有权。

// 这个函数接受一个 String 的引用 (&)。
// 它借用了 String 但不获取所有权。
fn calculate_length(s: &String) -> usize {
    s.len()
} // 在这里,s(引用)离开了作用域。但因为它不拥有
  // String 数据,所以当引用离开作用域时,数据不会被丢弃。

fn main() {
    let s1 = String::from("hello");

    // 我们使用 '&' 符号传递 s1 的引用。
    // s1 仍然拥有 String 数据。
    let len = calculate_length(&s1);

    // s1 在这里仍然有效,因为所有权从未被移动。
    println!("'{}' 的长度是 {}。", s1, len);
}

引用默认是不可变的,就像变量一样。如果你想修改被借用的数据,你需要一个可变引用,用 &mut 表示。然而,Rust 对可变引用强制执行严格的规则以防止数据竞争:

借用规则:

  1. 在任何给定时间,你只能拥有以下情况之一
    • 一个可变引用(&mut T)。
    • 任意数量的不可变引用(&T)。
  2. 引用必须始终有效(它们的生命周期不能超过它们所指向的数据 —— 这由生命周期管理,通常是隐式的)。

这些规则由编译器强制执行。

// 这个函数接受一个 String 的可变引用
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

fn main() {
    // 's' 必须声明为可变,以允许可变借用
    let mut s = String::from("hello");

    // 借用规则强制执行示例(取消注释行以查看错误):
    // let r1 = &s; // 不可变借用 - OK
    // let r2 = &s; // 另一个不可变借用 - OK(允许多个不可变借用)
    // let r3 = &mut s; // 错误!当存在活跃的不可变借用时,不能将 `s` 作为可变借用
    //                  // Rust 强制执行:要么多个读取者 (&T),要么单个写入者 (&mut T),绝不能同时存在
    // println!("{}, {}", r1, r2); // 使用 r1/r2 会使它们保持活跃,从而触发错误
    //                             // 如果没有这个 println,Rust 的 NLL(非词法作用域生命周期)会提前释放 r1/r2,
    //                             // 使得 &mut s 在这里有效

    // 这里允许进行可变借用,因为没有其他活跃的借用
    change(&mut s);
    println!("{}", s); // 输出: hello, world
}

复合数据类型

Rust 提供了将多个值组合成更复杂类型的方法。

结构体

结构体(Structs,structure 的缩写)允许你通过将相关的数据字段组合在一个名称下,来定义自定义数据类型。

// 定义一个名为 User 的结构体
struct User {
    active: bool,
    username: String, // 使用有所有权的 String 类型
    email: String,
    sign_in_count: u64,
}

fn main() {
    // 创建 User 结构体的一个实例
    // 实例必须为所有字段提供值
    let mut user1 = User {
        email: String::from("someone@example.com"),
        username: String::from("someusername123"),
        active: true,
        sign_in_count: 1,
    };

    // 使用点表示法访问结构体字段
    // 实例必须是可变的才能更改字段值
    user1.email = String::from("anotheremail@example.com");

    println!("用户邮箱: {}", user1.email);

    // 使用辅助函数创建 User 实例
    let user2 = build_user(String::from("user2@test.com"), String::from("user2"));
    println!("用户 2 的活跃状态: {}", user2.active);

    // 结构体更新语法:创建一个新实例,
    // 使用现有实例的某些字段来填充剩余字段。
    let user3 = User {
        email: String::from("user3@domain.com"),
        username: String::from("user3"),
        ..user2 // 从 user2 获取 'active' 和 'sign_in_count' 的值
    };
    println!("用户 3 的登录次数: {}", user3.sign_in_count);
}

// 返回 User 实例的函数
fn build_user(email: String, username: String) -> User {
    User {
        email, // 字段初始化简写:如果参数名与字段名匹配
        username,
        active: true,
        sign_in_count: 1,
    }
}

Rust 还支持元组结构体,它们是命名的元组(例如 struct Color(i32, i32, i32);),以及类单元结构体,它们没有字段,在你需要在某个类型上实现 Trait 但不需要存储任何数据时很有用(例如 struct AlwaysEqual;)。

枚举

枚举(Enums,enumerations)允许你通过列举其可能的变体来定义一个类型。一个枚举值只能是其可能的变体之一。

// 定义 IP 地址类型的简单枚举
enum IpAddrKind {
    V4, // 变体 1
    V6, // 变体 2
}

// 枚举变体也可以持有相关联的数据
enum IpAddr {
    V4(u8, u8, u8, u8), // V4 变体持有四个 u8 值
    V6(String),         // V6 变体持有一个 String
}

// Rust 标准库中一个非常常见且重要的枚举:Option<T>
// 它编码了一个可能存在或不存在的值的概念。
// enum Option<T> {
//     Some(T), // 表示存在一个类型为 T 的值
//     None,    // 表示值的缺失
// }

// 另一个关键的标准库枚举:Result<T, E>
// 用于可能成功 (Ok) 或失败 (Err) 的操作。
// enum Result<T, E> {
//     Ok(T),   // 表示成功,包含一个类型为 T 的值
//     Err(E),  // 表示失败,包含一个类型为 E 的错误值
// }

fn main() {
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;

    // 创建带有相关数据的 IpAddr 枚举实例
    let home = IpAddr::V4(127, 0, 0, 1);
    let loopback = IpAddr::V6(String::from("::1"));

    // Option<T> 示例
    let some_number: Option<i32> = Some(5);
    let no_number: Option<i32> = None;

    // 'match' 控制流运算符非常适合处理枚举。
    // 它允许你根据枚举变体执行不同的代码。
    match some_number {
        Some(i) => println!("得到一个数字: {}", i), // 如果是 Some,将内部值绑定到 i
        None => println!("什么也没有得到。"),           // 如果是 None
    }

    // 'match' 必须是穷尽性的:你必须处理所有可能的变体。
    // 下划线 '_' 可以用作通配符模式来捕获任何
    // 未显式列出的变体。
}

Option<T>Result<T, E> 是 Rust 健壮地处理潜在缺失值和错误方法的核心。

Cargo 简介:Rust 构建工具和包管理器

Cargo 是 Rust 生态系统中不可或缺的一部分,它简化了构建、测试和管理 Rust 项目的过程。这是开发者经常称赞的特性之一。

  • Cargo.toml:这是你的 Rust 项目的清单文件。它采用 TOML(Tom's Obvious, Minimal Language)格式编写。它包含关于你项目的重要元数据(如名称、版本和作者),并且至关重要地,列出了项目的依赖项(你的项目所依赖的其他外部 Crate)。
    [package]
    name = "my_project"
    version = "0.1.0"
    edition = "2021" # 指定要使用的 Rust 版本(影响语言特性)
    
    # 依赖项在下面列出
    [dependencies]
    # 示例:添加 'rand' crate 用于生成随机数
    # rand = "0.8.5"
    # 当你构建时,Cargo 将下载并编译 'rand' 及其依赖项。
    
  • cargo new <project_name>:创建一个新的二进制(可执行)应用程序项目结构。
  • cargo new --lib <library_name>:创建一个新的库(旨在供其他程序使用的 Crate)项目结构。
  • cargo build:编译你的项目及其依赖项。默认情况下,它会创建一个未优化的调试构建。输出位于 target/debug/ 目录中。
  • cargo build --release:编译你的项目并启用优化,适用于分发或性能测试。输出位于 target/release/ 目录中。
  • cargo run:编译(如果需要)并运行你的二进制项目。
  • cargo check:快速检查你的代码是否有编译错误,而不实际生成最终的可执行文件。这通常比 cargo build 快得多,在开发过程中用于快速反馈很有用。
  • cargo test:运行你项目中定义的任何测试(通常位于 src 目录或单独的 tests 目录中)。

当你向 Cargo.toml 文件添加依赖项,然后运行像 cargo buildcargo run 这样的命令时,Cargo 会自动处理从中央仓库 crates.io 下载所需的 Crate(以及的依赖项)并将所有内容编译在一起。

你的 Rust 学习之旅的后续步骤

这份入门指南涵盖了让你起步的最基础知识。随着你的进步,Rust 语言提供了许多更强大的功能供你探索:

  • 错误处理: 掌握 Result 枚举,使用 ? 操作符进行错误传递,以及定义自定义错误类型。
  • 集合: 有效地使用常见数据结构,如 Vec<T>(动态数组/向量)、HashMap<K, V>(哈希映射)、HashSet<T> 等。
  • 泛型: 使用类型参数(<T>)编写可以操作不同数据类型的灵活、可重用的代码。
  • Trait: 定义类型可以实现的共享行为和接口(类似于其他语言中的接口,但功能更强大)。
  • 生命周期: 理解 Rust 如何确保引用始终有效,有时需要显式的生命周期注解('a)。
  • 并发: 探索线程、用于消息传递的通道、使用 MutexArc 的共享状态,以及 Rust 强大的用于异步编程的 async/await 语法。
  • 宏: 学习编写自己的宏以进行高级编译时代码生成。
  • 模块: 将大型项目组织成逻辑单元,控制项的可见性(公共/私有)。
  • 测试: 使用 Rust 内置的测试框架编写有效的单元测试、集成测试和文档测试。

结论

Rust 提供了一个独特且引人注目的方案:结合了像 C++ 这样的底层语言所期望的原始性能,以及强大的编译时安全保证,消除了整类的常见错误,尤其是在内存管理和并发方面。虽然学习曲线涉及到内化像所有权和借用这样的新概念(并听从有时严格的编译器),但其回报是软件具有高度可靠性、效率,并且从长远来看通常更易于维护。凭借其通过 Cargo 提供的出色工具和支持性的社区,Rust 是一门值得学习的语言。

从小处着手,使用 Cargo 进行实验,将编译器有用的(尽管有时冗长)错误消息视为指导,你将顺利踏上掌握这门强大且日益流行的语言的道路。祝你 Rust 编程愉快!

相关文章