Rust 基础语法

环境装好后,我们来系统过一遍 Rust 的基础语法。这一章不涉及复杂概念(所有权、借用留到后面),只讲程序"长什么样"——函数怎么定义、注释怎么写、语句和表达式有什么区别、宏又是什么。读完你就能看懂任何 Rust 代码的大致框架。

1. Rust 程序的基本结构

一个可执行的 Rust 程序必须有 main 函数作为入口。Rust 用 fn 关键字声明函数,而不是像 Go/C 那样直接写函数名。看这个完整例子:

// 一个完整的 Rust 程序由这些组成
// 1. fn 定义函数
// 2. main 是入口(二进制程序必须有且只有一个)

fn main() {
    println!("Hello, Rust!");      // 语句以分号结尾
    let result = add(3, 5);        // 调用函数
    println!("3 + 5 = {}", result);
}

// 定义一个函数:fn 名字(参数: 类型) -> 返回类型
fn add(a: i32, b: i32) -> i32 {
    a + b                          // 注意!没有分号——这是表达式
}

几个要点:

2. 语句 vs 表达式(关键概念)

这是 Rust 一个非常重要、新手最容易栽跟头的概念,直接影响代码能不能编译。规则很简洁:

// ===== 语句(Statement)vs 表达式(Expression)=====
// 这是 Rust 一个非常重要的概念,直接影响能不能编译通过

// 语句:执行一个动作,不返回值,以分号结尾
let x = 5;            // 语句
println!("hi");       // 语句(宏调用也是语句)

// 表达式:求值为一个值,【不】加分号
5 + 3                 // 表达式,值为 8
x * 2                 // 表达式
{                     // 块也是表达式,值是最后一个表达式的值
    let y = 10;
    y + 1             // 块的值是 11
}

// 函数体的最后一行如果不加分号,就是返回值
fn double(n: i32) -> i32 {
    n * 2             // 表达式,作为返回值
}

fn double2(n: i32) -> i32 {
    n * 2;            // 加了分号就变成语句,返回 () —— 编译报错!
}

这条规则的好处是:函数返回值不需要 return 关键字——把最后一行写成表达式(不加分号)就行。当然你也可以显式写 return n * 2;,效果一样,但 Rust 风格更偏好"尾表达式"。这是初学者一定要记住的:函数最后一行不加分号表示返回值,加了分号就变成了返回单元类型,会触发类型不匹配错误。

3. 注释

Rust 的注释规则和大多数语言一致,但多了一种文档注释,会被工具读取生成网页文档:

// 单行注释:双斜杠,到行尾结束

/* 多行注释:
   可以跨多行,
   用斜杠 + 星号包裹。*/

/// 三斜杠是【文档注释】
/// 用 markdown 写,会被 cargo doc 生成成网页文档
/// 紧贴在它下面的那个函数/结构体的说明
///
/// # 示例
///
/// ```
/// let s = add(1, 2);
/// ```
pub fn add(a: i32, b: i32) -> i32 {
    a + b
}

//! 另一种文档注释:放在文件开头,描述整个模块
//! 这种叫"内部文档注释"

文档注释是 Rust 工程化的亮点之一。你写的 API 注释,运行 cargo doc --open 就能自动生成漂亮的 HTML 文档站点(类似 Java 的 Javadoc、Python 的 Sphinx)。写公共库时,务必为每个 pub 项目补上文档注释。

4. 宏:感叹号结尾的"魔法"

你会注意到 println! 末尾有个感叹号。这不是函数,而是宏(macro)。宏在编译期展开成代码,因此能做函数做不到的事——比如接收可变数量、可变类型的参数。

// 宏的名字以感叹号结尾,这是与普通函数的视觉区别
println!("Hello");              // 打印宏
println!("x = {}, y = {}", 1, 2); // 格式化打印,{} 是占位符

// eprintln 打印到标准错误流
eprintln!("这行进 stderr");

// format! 拼接字符串(不打印,返回 String)
let s = format!("{} 加 {} 等于 {}", 1, 2, 3);

// vec! 创建数组(Vector)
let v = vec![1, 2, 3];

// assert! / assert_eq! 断言宏(测试用)
assert_eq!(2 + 2, 4);

// 为什么用宏而不是函数?
// 因为 println! 的参数个数和类型是变化的,
// 函数做不到这一点。宏在编译期展开生成对应代码。

现在你不需要理解宏是怎么定义的,只要记住:以感叹号结尾的就是宏,用法像函数但更灵活。最常用的几个是 println!format!vec!assert_eq!

5. println! 格式化占位符

因为格式化非常常用,这里展开讲一下。println! 用花括号作占位符,按顺序替换后面的参数。常见的几种写法:

后面 data-types 和 strings 篇会有更多格式化示例。现在记住最常用的就是花括号占位即可。

6. 关键字速览

Rust 大约有 50 个关键字,比 Go(25 个)多,但比 C++/Java 少很多。先混个眼熟,后面每篇都会用到:

// ===== Rust 的部分关键字 =====
// 声明:let  fn  const  static
// 类型:i32 u64 f64 bool char String Vec
// 控制流:if else loop while for match return break continue
// 所有权/借用:move ref mut self Self
// 模块:mod use pub crate super
// 结构:struct enum trait impl type
// 其他:as where unsafe async await dyn box

不需要现在背下来,边用边记。有几个特殊的:self 指向当前对象(类似其他语言的 this),Self 是当前类型的别名,move 用于闭包捕获所有权,unsafe 标记需要手动保证安全的代码块。

7. 代码风格与 rustfmt

和 Go 的 gofmt 一样,Rust 有官方格式化工具 rustfmt,通过 cargo fmt 调用。它会自动统一缩进(4 空格)、对齐、换行风格。VS Code 的 rust-analyzer 扩展会保存时自动格式化,所以你几乎不用手动管风格——但所有人写出来的代码长得几乎一样。

命名约定:变量、函数用蛇形命名(snake_case),如 user_name、calculate_total;类型(结构体、枚举、Trait)用大驼峰(UpperCamelCase),如 User、HttpResponse;常量用全大写下划线,如 MAX_SIZE。这些都是约定俗成,rustfmt 和 clippy 会帮你检查。

小结

这一篇你了解了 Rust 程序的整体骨架:fn main 入口、函数定义、语句与表达式的区别(分号规则)、注释、宏。最重要的概念是"表达式不加分号、加了分号就变语句",这是后面所有 Rust 代码的基础。下一篇我们看变量与可变性——Rust 的"默认不可变"设计是它安全性的重要一环。

← 上一篇 Rust 环境安装与 Cargo

下一篇 Rust 变量与可变性

✈️💬