C 程序员看 Rust 控制流,最容易先找这些对应关系:
C if -> Rust if
C while -> Rust while
C for -> Rust for
C break -> Rust break
C continue -> Rust continue
这些对应关系大体成立,但有几个关键差异会影响日常代码:
if和while的条件必须是boolif可以产生值- Rust 没有 C 的三段式
for - Rust 的
for是遍历一个可迭代对象 loop表示明确的无限循环,也可以用break value返回值
这些差异不是高级概念,写第一个 CLI 或处理 buffer 时马上会遇到。
if 条件必须是 bool
C 里 if 接受整数、指针和表达式结果:
if (len) {
handle_data();
}
if (ptr) {
use_ptr(ptr);
}
if (flags & 0x01) {
enable();
}
Rust 的 if 条件必须是 bool:
if len != 0 {
handle_data();
}
if !ptr.is_null() {
// raw pointer case
}
if flags & 0x01 != 0 {
enable();
}
这和运算符那篇是同一条规则:Rust 不把整数隐式当作真假。条件里要写清楚判断标准。
在普通 Rust 代码里,更常见的是直接用 API 表达意图:
if !data.is_empty() {
handle_data(data);
}
if name.is_empty() {
return Err(ConfigError::EmptyName);
}
这比 if (len) 更啰嗦一点,但读者不用猜“非零”背后到底代表什么状态。
if 可以产生值
C 里经常用三目运算符:
int limit = debug ? 64 : 1024;
Rust 没有 ?:。if 本身可以产生值:
let limit = if debug { 64 } else { 1024 };
两个分支必须产生同一种类型:
let limit = if debug { 64 } else { "large" }; // 编译错误
这也适合写简单的选择逻辑:
let mode = if readonly {
"read"
} else {
"write"
};
如果只是执行动作,不需要返回值,写法和 C 很接近:
if verbose {
eprintln!("reading config");
}
区别在于:Rust 的 if 既能当控制流,也能当表达式。是否产生值,取决于你怎么使用它。
while 接近 C,但条件仍然是 bool
C 的 while:
while (offset < len) {
offset++;
}
Rust 写法很接近:
while offset < len {
offset += 1;
}
同样,条件必须是 bool。C 里的这类写法:
while (len--) {
consume();
}
不适合直接搬到 Rust。更清楚的写法是:
while len > 0 {
consume();
len -= 1;
}
处理读取循环时,也常把结果先绑定出来:
while offset < data.len() {
let byte = data[offset];
offset += 1;
handle_byte(byte);
}
这仍然是 C 程序员熟悉的循环模型,只是 Rust 不鼓励把递减、赋值和条件压到同一个表达式里。
loop 表示明确的无限循环
C 里无限循环常写:
for (;;) {
poll();
}
或:
while (1) {
poll();
}
Rust 用 loop:
loop {
poll();
}
这比 while true 更直接:这里就是无限循环,直到内部 break、return 或错误传播。
loop 还可以通过 break 返回值:
let packet = loop {
if let Some(packet) = try_read_packet()? {
break packet;
}
};
这类写法适合“不断尝试,直到拿到一个结果”。C 里通常要先声明一个变量,再在循环里赋值并跳出;Rust 可以让循环本身产生结果。
Rust 的 for 不是 C 的三段式循环
C 的 for 最常见是三段式:
for (size_t i = 0; i < len; i++) {
sum += data[i];
}
Rust 没有这种 for (init; condition; step)。Rust 的 for 遍历一个可迭代对象:
for byte in data {
sum += *byte as u32;
}
如果需要下标,用范围:
for i in 0..data.len() {
sum += data[i] as u32;
}
0..data.len() 不包含右边界,类似 C 里的 i < len。
如果同时需要下标和值,用 enumerate:
for (i, byte) in data.iter().enumerate() {
println!("{i}: {byte}");
}
Rust 的默认倾向是:能直接遍历数据,就少写手工下标。下标不是不能用,但不应该成为所有循环的默认形状。
范围要分清是否包含右边界
Rust 常用两种范围:
0..len // 不包含 len
0..=last // 包含 last
对应 C 里的两种判断:
for (i = 0; i < len; i++) // 0..len
for (i = 0; i <= last; i++) // 0..=last
遍历 slice 时,最常见的是不包含右边界:
for i in 0..data.len() {
handle(data[i]);
}
但如果只是读每个元素,优先写:
for byte in data {
handle(*byte);
}
这能少一类越界错误,也更接近 Rust 对 slice 的使用方式。
break 和 continue 基本熟悉
C 的 break 和 continue:
for (size_t i = 0; i < len; i++) {
if (data[i] == 0) {
break;
}
if (data[i] == 0xff) {
continue;
}
handle(data[i]);
}
Rust 写法类似:
for byte in data {
if *byte == 0 {
break;
}
if *byte == 0xff {
continue;
}
handle(*byte);
}
嵌套循环里,Rust 可以用标签指定跳出哪一层:
'outer: for row in rows {
for byte in row {
if *byte == 0 {
break 'outer;
}
}
}
这比在 C 里用额外 flag 或 goto 跳出多层循环更明确。
match 不是 switch 的简单换皮
C 里多分支常用 switch:
switch (state) {
case 0:
init();
break;
case 1:
run();
break;
default:
fail();
break;
}
Rust 有 match:
match state {
0 => init(),
1 => run(),
_ => fail(),
}
第一层可以先把它当作更严格的多分支。它不会自动 fallthrough,每个分支用 =>,_ 表示默认分支。
遇到 enum 以后,match 会变得更重要:它不只是替代 switch,还会检查枚举状态是否覆盖完整。
最小迁移表
C if (x) -> Rust if x != 0 / !is_empty() / !is_null()
C ?: -> Rust if ... { ... } else { ... }
C while (cond) -> Rust while cond
C while (len--) -> Rust while len > 0 { len -= 1; ... }
C for (;;) -> Rust loop
C for (i=0; i<len; i++) -> Rust for i in 0..len
C 遍历数组 -> Rust for item in slice
C break / continue -> Rust break / continue
C 多层跳出 -> Rust loop label + break 'label
C switch -> Rust match
Rust 控制流不是难在语法,而是默认减少了 C 里“把赋值、递增、条件、隐式真假压到一个表达式里”的写法。条件写清楚,遍历对象写清楚,后面的借用和所有权才更容易理解。