跳至内容

基本语法

这篇讲 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);
}
Rust 的默认不可变迫使你在声明变量时思考「这个值需要变吗?」——大多数场景下答案是不需要。这是一种主动约束,让代码意图更清晰。

变量隐藏(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 位i8u8
16 位i16u16
32 位i32u32
64 位i64u64
128 位i128u128
archisizeusize

默认整型是 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,值为 truefalse

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());
}
数组越界访问在 Rust 中会导致 panic(运行时错误),而不是像 C/C++ 那样「默默越界」——这又是一道安全防线。

函数

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 有三种循环:loopwhilefor

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}");
    }
}
Rust 中用 for 遍历集合是最习惯的写法——安全、高效,编译器会做边界检查消除优化。Go 程序员转 Rust 到这里会觉得很熟悉。

一句话小结

变量默认不可变(let),需要修改加 mut,同名变量可隐藏(shadowing)。Rust 是静态强类型,大多数时候靠类型推断。函数参数和返回值必须标注类型。if 是表达式可有返回值,for 是遍历集合的首选。下一篇讲 Rust 最核心的概念——所有权与借用

练习

  1. 写一个函数 fibonacci(n: u32) -> u64,返回第 n 个斐波那契数。用 for 循环实现。
  2. 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));   // 良
}
最后更新于