基本语法
这篇讲 Rust 的基本语法:变量声明(不可变/可变/隐藏)、标量数据类型、常量、函数和控制流。Rust 的语法看似熟悉,但有一些独特规则——理解它们是写出正确代码的前提。
变量
不可变变量(默认)
Rust 使用 let 声明变量。默认不可变——这与绝大多数语言不同:
fn main() {
let price = 188;
// price = 288; // ❌ 编译错误!cannot assign twice to immutable variable
println!("price: {}", price);
}为什么要默认不可变?因为不可变消除了意外的值修改,编译器可以做出更激进的优化——这正是 Rust「安全」哲学的体现。
可变变量
如果需要修改变量的值,加 mut 关键字:
fn main() {
let mut price = 188;
price = 288; // ✅ 通过 mut 声明,允许修改
println!("price: {}", price);
}变量隐藏(Shadowing)
Rust 允许用 let 重新声明同名变量,新变量会「隐藏」旧变量:
fn main() {
let x = 5;
let x = x + 1; // x = 6,隐藏上一个 x
let x = x * 2; // x = 12
println!("x: {}", x); // 12
}隐藏与 mut 的区别:
- 隐藏是新变量,可以改变类型;
mut只是修改值,类型不能变 - 隐藏后旧变量被「遮蔽」但并未被修改——它在自己的作用域里是不变的
fn main() {
let spaces = " "; // &str 类型
let spaces = spaces.len(); // usize 类型——隐藏允许改变类型
let mut spaces2 = " ";
// spaces2 = spaces2.len(); // ❌ 编译错误!mut 不能改变类型
}常量
常量用 const 声明,必须显式标注类型,值在编译期确定:
const MAX_PLAYERS: u32 = 100_000;
const PI: f64 = 3.14159;
const APP_NAME: &str = "My Rust App";
fn main() {
println!("游戏最多支持 {MAX_PLAYERS} 个玩家");
}const vs static:
// const:编译期内联替换,无固定内存地址
const VERSION: &str = "1.0.0";
// static:有固定内存地址,生命周期为 'static
static CONFIG_PATH: &str = "/etc/app/config.toml";
// static mut:可变静态变量,读写都需要 unsafe
static mut COUNTER: u32 = 0;
fn main() {
unsafe {
COUNTER += 1;
println!("COUNTER: {}", COUNTER);
}
}static mut 在多线程环境下是不安全的——Rust 无法保证没有数据竞争。读写它必须在 unsafe 块中,这是编译器在提醒你:「这里需要你自己负责」。数据类型
Rust 是静态强类型语言,编译器必须在编译期知道所有变量的类型。大多数情况下编译器能推断,但有时需要手动标注。
标量类型
整型:
| 长度 | 有符号 | 无符号 |
|---|---|---|
| 8 位 | i8 | u8 |
| 16 位 | i16 | u16 |
| 32 位 | i32 | u32 |
| 64 位 | i64 | u64 |
| 128 位 | i128 | u128 |
| arch | isize | usize |
默认整型是 i32,性能最好。isize/usize 取决于机器架构(64 位机器上就是 64 位)。
let a = 98_222; // 默认 i32,下划线增强可读性
let b = 0xff; // 十六进制
let c = 0o77; // 八进制
let d = 0b1111_0000; // 二进制
let e: u8 = 255; // 显式标注类型
浮点型:f32(单精度)、f64(双精度,默认)。
let x = 2.0; // f64
let y: f32 = 3.0; // f32
布尔型:bool,值为 true 或 false。
let is_ready = true;
let is_done: bool = false;字符型:char 是 4 字节的 Unicode 标量值(区别于 C 的 1 字节 char)。
let c = 'z';
let z = 'ℤ';
let heart_eyed_cat = '😻';复合类型
元组(Tuple):固定长度的异构集合。
fn main() {
let tup: (i32, f64, u8) = (500, 6.4, 1);
// 解构
let (x, y, z) = tup;
println!("x={}, y={}, z={}", x, y, z);
// 索引访问
println!("第一个: {}, 第二个: {}", tup.0, tup.1);
}数组(Array):固定长度的同类型集合,分配在栈上。
fn main() {
let arr = [1, 2, 3, 4, 5];
let zeros = [0; 5]; // [0, 0, 0, 0, 0]
let arr: [i32; 3] = [10, 20, 30];
println!("第一个: {}", arr[0]);
println!("长度: {}", arr.len());
}函数
用 fn 关键字定义,参数必须标注类型,返回值用 -> 标注:
fn add(a: i32, b: i32) -> i32 {
a + b // 最后一行没有分号 = 返回值(表达式)
}
fn greet(name: &str) -> String {
let mut s = String::from("Hello, ");
s.push_str(name);
s // return s; 也可以,但 Ruster 常用无分号返回
}
fn main() {
println!("1+2={}", add(1, 2));
println!("{}", greet("Rust"));
}关键规则:Rust 中最后一行不加分号的是表达式,作为返回值;加了分号就变成了语句(返回 ())。
控制流
if 表达式
Rust 的 if 是表达式——可以有返回值:
fn main() {
let number = 6;
if number % 4 == 0 {
println!("number 能被 4 整除");
} else if number % 3 == 0 {
println!("number 能被 3 整除");
} else {
println!("number 不能被 4 或 3 整除");
}
// if 作为表达式使用
let condition = true;
let x = if condition { 5 } else { 6 };
println!("x = {}", x); // 5
}if 作为表达式时,各个分支的返回值类型必须相同。而且条件必须是 bool——Rust 不会像 C/Python 那样自动把数字转成布尔值。循环
Rust 有三种循环:loop、while、for。
loop——无限循环,直到显式 break,可以返回值:
fn main() {
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // break 后跟值 = 循环的返回值
}
};
println!("result = {}", result); // 20
}while——条件循环:
fn main() {
let mut number = 3;
while number != 0 {
println!("{number}");
number -= 1;
}
}for——遍历集合(最常用):
fn main() {
let arr = [10, 20, 30, 40, 50];
for elem in arr {
println!("值为:{elem}");
}
// 遍历范围
for n in 1..5 { // 1..5 = [1, 2, 3, 4](不含 5)
println!("{n}");
}
for n in (1..5).rev() { // 反向遍历
println!("{n}");
}
}for 遍历集合是最习惯的写法——安全、高效,编译器会做边界检查消除优化。Go 程序员转 Rust 到这里会觉得很熟悉。一句话小结
变量默认不可变(let),需要修改加 mut,同名变量可隐藏(shadowing)。Rust 是静态强类型,大多数时候靠类型推断。函数参数和返回值必须标注类型。if 是表达式可有返回值,for 是遍历集合的首选。下一篇讲 Rust 最核心的概念——所有权与借用。
练习
- 写一个函数
fibonacci(n: u32) -> u64,返回第 n 个斐波那契数。用for循环实现。 - 用
if表达式实现:根据一个 0-100 的分数返回"优"(>=90)、"良"(>=70)、"及格"(>=60)、"不及格"(<60)。
参考答案
fn fibonacci(n: u32) -> u64 {
if n == 0 { return 0; }
if n == 1 { return 1; }
let mut prev = 0u64;
let mut curr = 1u64;
for _ in 2..=n {
let next = prev + curr;
prev = curr;
curr = next;
}
curr
}
fn grade(score: u32) -> &'static str {
if score >= 90 {
"优"
} else if score >= 70 {
"良"
} else if score >= 60 {
"及格"
} else {
"不及格"
}
}
fn main() {
println!("f(10) = {}", fibonacci(10)); // 55
println!("score 85 = {}", grade(85)); // 良
}