【Rust入门】第二章:通用编程概念

0 阅读5分钟

第一章我们打通了“任督二脉”(环境配置),现在进入 第二章:通用编程概念

这一章非常关键。虽然大部分编程语言都有变量、函数和循环,但 Rust 在这些基础概念上有一些独特的“反直觉”设计。正是这些设计,构成了 Rust 安全性的基石。

请新建一个项目用于本章练习:

cargo new rust_basics
cd rust_basics

🚀 第二章:通用编程概念

2.1 变量与可变性 (Variables & Mutability)

这是 Rust 新手遇到的第一个“坑”,也是 Rust 最重要的特性之一:默认不可变

代码实验 1:尝试修改变量 打开 src/main.rs,输入以下代码:

fn main() {
    let x = 5;
    println!("x 的值是: {}", x);
    x = 6; // 试图修改 x
    println!("x 的值是: {}", x);
}

运行 cargo run结果:❌ 报错!cannot assign twice to immutable variable x(不能对不可变变量 x 二次赋值)。

核心概念: 在 Rust 中,一旦你用 let 绑定了一个值,它默认就是只读的。这为了防止你无意中修改了不该修改的数据,从而导致 bug(特别是在多线程环境下)。

修正:加上 mut 关键字 如果你想让变量可变,必须显式告诉编译器:

fn main() {
    let mut x = 5; // 注意这里的 mut
    println!("x 的值是: {}", x);
    x = 6;
    println!("x 的值是: {}", x);
}

再次运行,成功!✅


2.2 隐藏 (Shadowing)

这是一个 Rust 很有趣的特性。你可以在同一个作用域内多次声明同名变量。

fn main() {
    let x = 5;
    let x = x + 1; // 第二个 x "隐藏" 了第一个 x
    let x = x * 2; // 第三个 x "隐藏" 了第二个 x

    println!("x 的值是: {}", x); // 输出 12
}

为什么需要这个? 通常用于类型转换。比如你拿到了一个字符串类型的 "42",想把它转成数字类型的 42

  • 在其他语言你可能需要:let input_str = "42"; let input_num = parse(input_str);
  • 在 Rust 中:let input = "42"; let input = input.parse(); (复用名字,代码更干净)。

2.3 数据类型 (Data Types)

Rust 是静态强类型语言。编译时必须知道所有变量的类型。

标量类型 (Scalar Types) 代表单个值。

  1. 整型
    • i32 (默认,有符号 32 位整数)
    • u32 (无符号,不能是负数)
    • i64 / u64 (64 位,常用于大数)
    • isize / usize (由你的 CPU 架构决定,64位系统就是64位,常用于索引数组)
  2. 浮点型f64 (默认,双精度), f32
  3. 布尔型bool (值为 truefalse)。
  4. 字符型char
    • 注意:Rust 的 char 是 4 字节的 Unicode 标量值,用单引号。它可以存储中文、Emoji 等。
    • let c = '中'; (合法)
    • let z = 'ℤ'; (合法)
    • let cat = '😻'; (合法)

复合类型 (Compound Types) 可以将多个值组合成一个类型。

1. 元组 (Tuple) 长度固定,元素类型可以不同。

fn main() {
    // 定义元组
    let tup: (i32, f64, u8) = (500, 6.4, 1);

    // 访问方式 1:解构 (Destructuring)
    let (x, y, z) = tup;
    println!("y 的值是: {}", y);

    // 访问方式 2:使用点号索引
    let five_hundred = tup.0;
    let six_point_four = tup.1;
    println!("第一个元素: {}", five_hundred);
}

2. 数组 (Array) 长度固定,元素类型必须相同。数据存放在栈(Stack)上。 (注意:如果你需要动态长度的列表,以后我们会学 Vector,现在先学 Array)

fn main() {
    let a = [1, 2, 3, 4, 5];
    
    // 另一种写法:[类型; 长度]
    let b: [i32; 5] = [1, 2, 3, 4, 5];
    
    // 初始化包含 5 个 3 的数组
    let c = [3; 5]; // 等同于 [3, 3, 3, 3, 3]

    println!("a 的第一个元素: {}", a[0]);
}

2.4 函数 (Functions)

Rust 的函数定义非常清晰。但有一个超级重要的概念:语句 (Statements) vs 表达式 (Expressions)

  • 语句:执行操作,没有返回值。以分号 ; 结尾。
  • 表达式:计算并产生一个值没有分号。

代码实验 2:函数与返回值

fn main() {
    let number = 5;
    let result = plus_one(number);
    println!("结果是: {}", result);
}

// 定义一个函数,参数必须标注类型
// -> i32 表示返回值类型是 i32
fn plus_one(x: i32) -> i32 {
    x + 1 // <--- 注意这里没有分号!这是一个表达式,它的值将作为返回值。
}

如果你把 x + 1 改成 x + 1;(加了分号),它就变成了语句,不再返回值(默认返回空的单元类型 ()),编译器会报错说“期望返回 i32,但实际返回了 ()”。

这是 Rust 新手最容易犯的错误之一,请务必留意分号!


2.5 控制流 (Control Flow)

1. if 表达式 在 Rust 中,if 是一个表达式,这意味着它可以赋值给变量。

fn main() {
    let condition = true;
    // 类似于三元运算符 condition ? 5 : 6
    let number = if condition { 5 } else { 6 }; 

    println!("Number is: {}", number);
}

注意:ifelse 返回的数据类型必须一致。

2. 循环 (Loops) Rust 有三种循环:loop, while, for

  • loop: 无限循环,直到你手动 break
  • while: 条件循环。
  • for: 遍历集合(最常用,最安全)。

代码实验 3:最常用的 for 循环

fn main() {
    // 遍历一个数组
    let a = [10, 20, 30, 40, 50];

    // element 会自动获取数组中的每个值
    for element in a {
        println!("值为: {}", element);
    }

    // 使用 Range (范围)
    // (1..4) 包含 1, 2, 3,不包含 4。如果想要包含 4,用 (1..=4)
    // .rev() 是反转的意思
    for number in (1..4).rev() {
        println!("倒计时: {}!", number);
    }
    println!("发射!");
}

📝 第二章总结与作业

总结:

  1. 变量:默认不可变,改值需加 mut
  2. Shadowing:同名变量可以覆盖旧变量,常用于类型转换。
  3. 类型:知道 i32, f64, bool, char,以及 Tuple 和 Array。
  4. 函数不加分号的代码行是表达式,代表返回值。
  5. 循环:首选 for 循环遍历数据。

作业(巩固你的知识):

  1. 写一个函数 fahrenheit_to_celsius(f: f64) -> f64,将华氏温度转换为摄氏温度。公式:C = (F - 32) / 1.8
  2. main 函数中,用一个循环(比如 5 次),计算并打印不同华氏度的摄氏度值。
  3. 进阶挑战:写一个函数生成斐波那契数列的第 n 项。(如果 n=1 返回 1,n=2 返回 1,n=3 返回 2...)。

做完这些,你就掌握了 Rust 的基本语法框架!