二、变量、数据类型与控制流

1. 变量与可变性:Rust的核心设计哲学

1.1 不可变性的设计意义

Rust默认变量不可变,这是其安全模型的基础:

let x = 5;
// x = 6;  // 编译错误!不能修改不可变变量

这种设计带来三大优势:

  1. 线程安全:不可变数据天然线程安全
  2. 推理简单:变量值在生命周期内保持不变
  3. 优化空间:编译器可做更多优化

1.2 可变变量(mut)

当确实需要修改变量时使用mut

let mut count = 0;
count += 1;  // 合法操作

1.3 变量遮蔽(Shadowing)的深层机制

let x = 5;
let x = x + 1;  // 新建变量,类型可以改变
let x = "现在我是字符串"; 

mut的关键区别:

  • 每次let都创建新绑定(不是修改原变量)
  • 可以改变类型
  • 原变量被遮蔽但生命周期不变

1.4常量

常量使用 const 关键字声明,必须标注类型:

const MAX_POINTS: u32 = 100_000; // 使用下划线提高可读性

fn main() {
    println!("最大分数: {}", MAX_POINTS);
}

常量与不可变变量的区别:

  • 常量在全局作用域中有效

  • 常量不能使用 mut

  • 常量只能设置为常量表达式,不能是函数调用结果或运行时计算值

2. 数据类型

2.1 标量类型详解

整数类型
类型位数范围说明
i88-128 到 1278位有符号整数
i1616-32,768 到 32,76716位有符号整数
i3232-2,147,483,648 到 2,147,483,64732位有符号整数
i6464-9,223,372,036,854,775,808 到 9,223,372,036,854,775,80764位有符号整数
i128128-170,141,183,460,469,231,731,687,303,715,884,105,728 到 170,141,183,460,469,231,731,687,303,715,884,105,727128位有符号整数
isize可变取决于架构(32位或64位系统)指针大小的有符号整数
u880 到 2558位无符号整数
u16160 到 65,53516位无符号整数
u32320 到 4,294,967,29532位无符号整数
u64640 到 18,446,744,073,709,551,61564位无符号整数
u1281280 到 340,282,366,920,938,463,463,374,607,431,768,211,455128位无符号整数
usize可变取决于架构(32位或64位系统)指针大小的无符号整数

 特殊整数操作:

// 溢出处理(debug模式会panic,release模式会wrap)
let x: u8 = 255;
let y = x.wrapping_add(1);  // 0

// 安全数学运算
let sum = 1u32.checked_add(2);  // Some(3)
let overflow = 255u8.checked_add(1);  // None
浮点类型实现细节

Rust采用IEEE-754标准:

  • f32:单精度(约6-9位有效数字)
  • f64:双精度(约15-17位有效数字)

特殊浮点值:

let inf = f32::INFINITY;
let neg_inf = f32::NEG_INFINITY;
let nan = f32::NAN;  // 非数字
字符类型
  • 4字节存储
  • Unicode标量值(U+0000到U+D7FF和U+E000到U+10FFFF)
let emoji = '😻';  // 有效
let c = '\u{1F408}';  // 猫符号
 布尔类型
  • bool:值为 true 或 false
let is_true: bool = true;
let is_false: bool = false;

2.2 复合类型内存布局

元组的内存排列
let tup: (i32, f64, u8) = (500, 6.4, 1);

 内存布局(64位系统):

+--------+--------+--------+
| i32(4) | pad(4) | f64(8) | u8(1) | pad(7) |
+--------+--------+--------+-------+--------+
数组的栈分配特性
let arr: [i32; 5] = [1, 2, 3, 4, 5];  // 完全在栈上分配

与切片的关系:

let slice: &[i32] = &arr[1..3];  // 借用数组部分元素

3. 控制流:Rust的表达式的力量

3.1 if表达式的类型系统要求

let number = if condition { 5 } else { 6 };  // 分支必须类型相同
// let bad = if true { 5 } else { "six" };  // 编译错误!

3.2 循环控制流详解

loop的独特优势
let mut counter = 0;
let result = loop {
    counter += 1;
    if counter == 10 {
        break counter * 2;  // 可返回值
    }
};
 while循环的编译优化
while condition {
    // 编译器会尝试将条件提升到循环外
}
for循环的迭代器协议
for i in 1..=5 {  // RangeInclusive
    println!("{}", i);
}

// 等价于
let mut iter = (1..=5).into_iter();
while let Some(i) = iter.next() {
    println!("{}", i);
}

3.3 模式匹配的编译优化

match value {
    1..=5 => println!("1到5"),
    6 | 7 => println!("6或7"),
    _ => println!("其他"),
}

 编译器会:

  1. 检查匹配的穷尽性
  2. 优化为跳转表或条件判断
  3. 保证所有路径都有返回值

 4. 深入案例:构建温度转换CLI

use std::env;

fn main() {
    let args: Vec<String> = env::args().collect();
    
    if args.len() != 3 {
        eprintln!("用法: {} [C|F] 温度值", args[0]);
        std::process::exit(1);
    }
    
    let scale = &args[1];
    let temperature: f64 = match args[2].parse() {
        Ok(t) => t,
        Err(_) => {
            eprintln!("错误: 温度必须是数字");
            std::process::exit(1);
        }
    };
    
    match scale.as_str() {
        "C" => println!("{}°C = {}°F", temperature, celsius_to_fahrenheit(temperature)),
        "F" => println!("{}°F = {}°C", temperature, fahrenheit_to_celsius(temperature)),
        _ => eprintln!("错误: 请指定C或F作为温度单位"),
    }
}

fn celsius_to_fahrenheit(c: f64) -> f64 {
    c * 9.0 / 5.0 + 32.0
}

fn fahrenheit_to_celsius(f: f64) -> f64 {
    (f - 32.0) * 5.0 / 9.0
}

 运行程序:

cargo run C 25.5
# 输出
# 25.5°C = 77.9°F

cargo run F 100
# 输出
# 100°F = 37.77777777777778°C

5. 性能考量与最佳实践

 整数选择原则

  • 默认使用i32(即使在64位系统)
  • 集合索引使用usize
  • 明确需求时再选择特定大小

浮点注意事项: 

   // 避免直接比较浮点数
   fn approx_equal(a: f64, b: f64) -> bool {
       (a - b).abs() < f64::EPSILON
   }

 循环优化技巧

   // 使用for比while更易优化
   for i in 0..collection.len() {
       // 编译器知道边界不会改变
   }

 模式匹配的穷尽检查

   enum Coin {
       Penny,
       Nickel,
       Dime,
       Quarter,
   }
   
   fn value(coin: Coin) -> u8 {
       match coin {  // 如果漏掉某个变体会编译错误
           Coin::Penny => 1,
           Coin::Nickel => 5,
           Coin::Dime => 10,
           Coin::Quarter => 25,
       }
   }

6. 常见陷阱与解决方案

问题1:整数溢出

// 不好的做法
let x: u8 = 255;
let y = x + 1;  // debug模式panic!

// 正确做法
let y = x.wrapping_add(1);  // 明确处理溢出

 问题2:浮点比较

// 错误方式
if 0.1 + 0.2 == 0.3 { /* 可能不成立 */ }

// 正确方式
if (0.1 + 0.2 - 0.3).abs() < f64::EPSILON { /* */ }

问题3:数组越界

let arr = [1, 2, 3];
// let x = arr[3];  // 编译时已知越界会报错

// 运行时检查
if let Some(val) = arr.get(3) {  // 返回Option
    // 安全访问
}

7. 进阶话题预览

  1. 类型推断算法:Hindley-Milner的Rust实现
  2. 模式匹配的穷尽性检查:编译器如何验证
  3. 控制流图(CFG)优化:Rust编译器的处理流程
  4. LLVM后端优化:如何生成高效机器码

总结回顾

本章深入探讨了:

  • Rust变量系统的设计哲学
  • 类型系统的内存布局细节
  • 控制流表达式的编译行为
  • 实际开发中的最佳实践

关键思维转变

  1. 从"变量可变"到"默认不可变+显式可变"
  2. 从"运行时检查"到"编译时验证"
  3. 从"语句导向"到"表达式优先"

在接下来的章节中,我们将探索Rust函数与模块系统,学习如何组织更复杂的代码结构。届时会看到这些基础概念如何支撑起更高级的语言特性。

案例源代码:https://pan.baidu.com/s/1TvNjvzGFhZb8Idq1oCQGYA?pwd=4z1j 提取码: 4z1j

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a736015

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值