Rust的类型系统是其安全保证的基石。本课将深入学习Rust的变量绑定机制、可变性控制,以及丰富的内置数据类型。
学习目标:掌握变量绑定与可变性、理解基本数据类型、学会类型转换与推断、了解常量与遮蔽
Rust使用let关键字绑定变量。默认情况下,Rust中的变量是不可变的(immutable),这是Rust安全哲学的核心之一。
fn main() {
let x = 5;
println!("x = {}", x);
// x = 6; // ❌ 编译错误!不能对不可变变量重新赋值
}
cannot assign twice to immutable variable。这不是bug,而是Rust帮你避免意外的修改。
fn main() {
let mut x = 5;
println!("x = {}", x);
x = 6; // ✅ 可以修改
println!("x = {}", x);
x = x + 10; // ✅ 可以多次修改
println!("x = {}", x);
}
✅ 验证通过
mut。这使代码意图更清晰,也帮助编译器做更好的优化。
Rust允许用同一个名字声明新变量,新变量会遮蔽(shadow)之前的变量。遮蔽与mut完全不同:
fn main() {
// 遮蔽允许改变类型
let x = 5; // x: i32
let x = x + 1; // x: i32, 值为6(新变量遮蔽旧变量)
let x = x * 2; // x: i32, 值为12
println!("x = {}", x);
// 遮蔽可以改变类型!这是与mut的关键区别
let spaces = " "; // spaces: &str
let spaces = spaces.len(); // spaces: usize
println!("空格数: {}", spaces);
// 用mut则不能改变类型
// let mut y = 5;
// y = "hello"; // ❌ 类型不匹配!
}
✅ 验证通过
Rust提供多种整数类型,区分有符号(i)和无符号(u):
| 长度 | 有符号 | 无符号 | 范围 |
|---|---|---|---|
| 8-bit | i8 | u8 | -128~127 / 0~255 |
| 16-bit | i16 | u16 | -32768~32767 / 0~65535 |
| 32-bit | i32 | u32 | ±21亿 / 0~42亿 |
| 64-bit | i64 | u64 | ±9.2×10¹⁸ |
| 128-bit | i128 | u128 | 超大范围 |
| 架构 | isize | usize | 与CPU位数一致 |
fn main() {
// 显式类型标注
let a: i32 = 42;
let b: u8 = 255;
let c: i64 = -9223372036854775808;
// 类型推断:默认i32
let d = 10; // 推断为i32
// 各种字面量写法
let dec = 1_000_000; // 十进制,下划线分隔
let hex = 0xff; // 十六进制
let oct = 0o77; // 八进制
let bin = 0b1111_0000; // 二进制
let byte = b'A'; // 字节(仅u8)
println!("十进制: {} 十六进制: {} 八进制: {} 二进制: {}", dec, hex, oct, bin);
println!("字节: {} 字符: {}", byte, byte as char);
// 整数溢出
// 在debug模式下溢出会panic
// 在release模式下溢出会回绕(wrap)
let mut e: u8 = 255;
// e = e + 1; // ❌ debug模式会panic!
// 使用显式方法处理溢出
e = e.wrapping_add(1); // 回绕:0
println!("wrapping_add(1)后: {}", e); // 0
// 检查溢出
let (val, overflowed) = 255u8.overflowing_add(1);
println!("255 + 1 = {} (溢出: {})", val, overflowed);
// 饱和运算
println!("饱和加法: {}", 255u8.saturating_add(1)); // 255
// usize/isize用于索引和大小
let arr = [1, 2, 3, 4, 5];
let len: usize = arr.len();
println!("数组长度: {}", len);
}
✅ 验证通过
fn main() {
let x: f64 = 2.0; // 64位浮点,默认
let y: f32 = 3.0; // 32位浮点
// 浮点运算
let sum = x + 2.5;
let product = x * 3.0;
let quotient = x / 4.0;
let remainder = 5.0 % 2.0; // 取余
println!("和: {} 积: {} 商: {} 余: {}", sum, product, quotient, remainder);
// 科学记数法
let big = 1.0e10;
let small = 1.0e-10;
println!("大数: {} 小数: {}", big, small);
// 特殊值
let inf = f64::INFINITY;
let neg_inf = f64::NEG_INFINITY;
let nan = f64::NAN;
println!("无穷: {} 负无穷: {} NaN: {}", inf, neg_inf, nan);
// ⚠️ 浮点数比较要小心
let a = 0.1 + 0.2;
let b = 0.3;
println!("0.1 + 0.2 == 0.3? {}", a == b); // false!
println!("0.1 + 0.2 = {:.17}", a); // 0.30000000000000004
// 安全的浮点比较
let epsilon = 1e-10;
println!("近似相等? {}", (a - b).abs() < epsilon); // true
}
✅ 验证通过
fn main() {
let t: bool = true;
let f: bool = false;
// 布尔运算
println!("true && false = {}", t && f); // 与
println!("true || false = {}", t || f); // 或
println!("!true = {}", !t); // 非
// 比较产生布尔值
let x = 5;
println!("5 > 3: {}", x > 3);
println!("5 == 5: {}", x == 5);
println!("5 != 3: {}", x != 3);
// 布尔值占1字节
println!("bool大小: {} 字节", std::mem::size_of::());
// if条件必须是bool
// let n = 5;
// if n { } // ❌ 错误!Rust不会自动转换
if n != 0 { } // ✅ 显式比较
}
✅ 验证通过
fn main() {
let c: char = 'z';
let z = 'ℤ';
let heart = '❤';
let ferris = '🦀';
println!("字符: {} {} {} {}", c, z, heart, ferris);
// char是4字节(Unicode标量值)
println!("char大小: {} 字节", std::mem::size_of::());
// Unicode码点
println!("'A'的码点: {}", 'A' as u32);
println!("'中'的码点: {}", '中' as u32);
println!("'🦀'的码点: {}", '🦀' as u32);
// 从码点创建字符
let from_code = char::from_u32(0x1F980);
println!("码点0x1F980: {:?}", from_code); // Some('🦀')
// 字符判断方法
println!("'0'.is_numeric(): {}", '0'.is_numeric());
println!("'a'.is_alphabetic(): {}", 'a'.is_alphabetic());
println!("'中'.is_alphabetic(): {}", '中'.is_alphabetic());
println!("' '.is_whitespace(): {}", ' '.is_whitespace());
// 遍历字符范围
for c in 'a'..='f' {
print!("{} ", c);
}
println!();
}
✅ 验证通过
fn main() {
// 创建元组
let tup: (i32, f64, char) = (500, 6.4, '🦀');
// 解构
let (x, y, z) = tup;
println!("x={}, y={}, z={}", x, y, z);
// 索引访问(用.不是[])
println!("第一个: {}", tup.0);
println!("第二个: {}", tup.1);
println!("第三个: {}", tup.2);
// 单元素元组需要逗号
let single = (42,); // 元组
let not_tuple = (42); // 只是括号里的数字
// 空元组(单元类型)
let unit = ();
println!("单元类型大小: {} 字节", std::mem::size_of::<()>());
// 嵌套元组
let nested = ((1, 2), (3, 4));
println!("嵌套: ({}, {}), ({}, {})", nested.0.0, nested.0.1, nested.1.0, nested.1.1);
}
✅ 验证通过
fn main() {
// 固定长度,类型相同
let arr: [i32; 5] = [1, 2, 3, 4, 5];
// 访问元素
println!("第一个: {}", arr[0]);
println!("最后一个: {}", arr[4]);
// 初始化相同值
let zeros = [0; 10]; // [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
println!("零数组: {:?}", zeros);
// 数组长度
println!("长度: {}", arr.len());
// 越界访问会panic(编译期检查或运行时检查)
// let index = 10;
// println!("{}", arr[index]); // ❌ 运行时panic!
// 安全访问
if let Some(val) = arr.get(2) {
println!("索引2: {}", val);
}
if arr.get(10).is_none() {
println!("索引10不存在");
}
// 遍历
for (i, &v) in arr.iter().enumerate() {
println!("arr[{}] = {}", i, v);
}
// 可变数组
let mut mut_arr = [1, 2, 3];
mut_arr[0] = 10;
println!("修改后: {:?}", mut_arr);
}
✅ 验证通过
fn main() {
// 整数之间转换
let a: i32 = 42;
let b: i64 = a as i64;
let c: u8 = a as u8; // 截断!
println!("i32->i64: {}, i32->u8: {}", b, c);
// 浮点转整数(截断,不四舍五入)
let f = 3.99;
let i = f as i32;
println!("3.99 as i32 = {}", i); // 3
// 整数转浮点
let n = 42;
let f2 = n as f64;
println!("42 as f64 = {}", f2);
// char与u32互转
let ch = '🦀';
let code = ch as u32;
let ch2 = char::from_u32(code).unwrap();
println!("char->u32->char: {} -> {} -> {}", ch, code, ch2);
}
✅ 验证通过
// 常量:编译期确定,不可变,无固定内存地址
const MAX_POINTS: u32 = 100_000;
const PI: f64 = 3.14159265358979;
// 静态变量:程序整个运行期存在,有固定内存地址
static LANGUAGE: &str = "Rust";
static mut COUNTER: u32 = 0; // 可变静态变量(unsafe才能访问)
fn main() {
println!("最大分数: {}", MAX_POINTS);
println!("π = {}", PI);
println!("语言: {}", LANGUAGE);
// 常量可以在任何作用域声明
{
const LOCAL_CONST: i32 = 42;
println!("局部常量: {}", LOCAL_CONST);
}
// println!("{}", LOCAL_CONST); // ❌ 超出作用域
// unsafe块中访问可变静态变量
unsafe {
COUNTER += 1;
println!("计数器: {}", COUNTER);
}
}
✅ 验证通过
const必须标注类型,let可以推断const在编译期内联,let在运行期求值const命名惯例全大写const不能使用mut
fn main() {
// Rust编译器很聪明,能从上下文推断类型
let x = 5; // i32(默认整数类型)
let y = 2.0; // f64(默认浮点类型)
let z = "hello"; // &str
let v = vec![1,2,3]; // Vec<i32>
// 有时需要手动标注
let guess: u32 = "42".parse().expect("不是数字");
println!("解析: {}", guess);
// 或者用类型后缀
let a = 42i64; // i64
let b = 3.14f32; // f32
let c = 0xFFu8; // u8
}
编写函数将华氏温度转为摄氏温度:C = (F - 32) * 5 / 9,用不同整数类型测试。
创建一个包含5个元素的i32数组,计算所有元素的和、最大值和最小值。
将字符串"2024"解析为u32,然后转为f64,再计算它的平方根。
分别用遮蔽和mut实现:将一个字符串转为其长度,再加10。观察两种方式的区别。
🔒 下一课解锁:控制流与函数 —— Rust的分支、循环与函数定义