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 // 注意!没有分号——这是表达式
}几个要点:
- main 函数:程序从这里开始执行,执行完就结束。无参数无返回值。
- fn 关键字:声明函数,后面跟函数名、参数列表、可选的返回类型箭头。
- 类型标注:参数必须显式写类型(如
i32),Rust 不像 Go 把类型放后面,而是用冒号。 - 分号:每条语句以分号结尾,这是 Rust 与 Go 一个明显的区别(Go 分号可省)。
2. 语句 vs 表达式(关键概念)
这是 Rust 一个非常重要、新手最容易栽跟头的概念,直接影响代码能不能编译。规则很简洁:
- 语句(statement):执行一个动作,不返回值,以分号结尾。如
let声明、函数调用。 - 表达式(expression):求值为一个值,不加分号。如算式、函数最后一行。
// ===== 语句(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 变量与可变性 →