强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Rust 系统编程语言完全教程 / 第04章:变量与数据类型基础

第04章:变量与数据类型基础

4.1 变量绑定(Variable Binding)

默认不可变

Rust 中使用 let 声明变量,默认不可变(immutable)

fn main() {
    let x = 5;
    println!("x = {}", x);

    // x = 6; // ❌ 编译错误:cannot assign twice to immutable variable
}

注意: Rust 称之为"变量绑定"(binding)而非"变量赋值"(assignment),因为 let x = 5 是将值 5 绑定到名字 x 上,而非简单的赋值操作。

为什么默认不可变

特性 默认不可变 默认可变(如 Python/JS)
安全性 防止意外修改 可能被意外修改
并发 编译时保证无数据竞争 需要运行时保护
可读性 明确标注可变性 需要额外追踪
优化 编译器可做更多优化 优化空间有限

4.2 可变变量(mut)

使用 mut 关键字声明可变变量:

fn main() {
    let mut x = 5;
    println!("x = {}", x);

    x = 6; // ✅ 使用 mut 声明的变量可以修改
    println!("x = {}", x);

    x += 1;
    println!("x = {}", x); // x = 7
}

可变性的边界

fn main() {
    let mut s = String::from("hello");

    // ✅ 可以修改内容
    s.push_str(", world");
    println!("{}", s);

    // ✅ 可以重新绑定(指向新的 String)
    s = String::from("new string");
    println!("{}", s);

    // ❌ 不能改变类型(类型在第一次绑定时确定)
    // s = 42; // 类型不匹配
}

实际应用:累加器

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    let mut sum = 0; // 必须使用 mut,因为值会变化

    for num in &numbers {
        sum += num;
    }

    println!("总和: {}", sum); // 总和: 15
}

4.3 变量遮蔽(Shadowing)

基本遮蔽

使用 let 重新声明同名变量,遮蔽(shadowing) 之前的变量:

fn main() {
    let x = 5;
    println!("x = {}", x); // x = 5

    let x = x + 1; // 遮蔽,创建新变量 x
    println!("x = {}", x); // x = 6

    let x = x * 2; // 再次遮蔽
    println!("x = {}", x); // x = 12
}

类型变换遮蔽

mut 不同,遮蔽可以改变变量类型:

fn main() {
    let spaces = "   ";       // &str 类型
    let spaces = spaces.len(); // usize 类型
    println!("空格数量: {}", spaces); // 空格数量: 3

    // 如果用 mut:
    // let mut spaces = "   ";
    // spaces = spaces.len(); // ❌ 编译错误:类型不匹配
}

遮蔽 vs mut

特性 遮蔽 (Shadowing) mut
关键字 let let mut
可变内容 创建新变量 修改同一变量
可改类型 ✅ 可以 ❌ 不可以
作用域 遮蔽变量有独立作用域 变量始终在同一作用域
可借用 新变量的借用不冲突 同一变量的借用需遵守规则
fn main() {
    // 遮蔽示例:类型转换
    let input = "42";
    let input: i32 = input.parse().expect("解析失败");
    println!("数值: {}", input);

    // 实际应用中,遮蔽常用于:
    // 1. 类型转换
    // 2. 不变性保护(let x = x + 1 使 x 继续不可变)
    // 3. 解构后重用变量名
}

4.4 常量(const)

基本用法

// 常量必须标注类型,必须在编译时确定值
const MAX_POINTS: u32 = 100_000;
const PI: f64 = 3.14159265358979;
const APP_NAME: &str = "MyApp";

fn main() {
    println!("最大点数: {}", MAX_POINTS);
    println!("圆周率: {}", PI);
    println!("应用名: {}", APP_NAME);
}

常量命名规范

使用 SCREAMING_SNAKE_CASE(全大写下划线分隔):

const MAX_CONNECTIONS: usize = 100;
const DEFAULT_TIMEOUT_MS: u64 = 3000;
const BUFFER_SIZE: usize = 4096;

常量 vs 静态变量 vs let

特性 const static let
可变性 不可变 不可变(static mut 可变但 unsafe) 可选 mut
值确定时间 编译时 编译时 运行时
存储位置 内联到使用处 静态内存区 栈上
地址 无固定地址 有固定地址 有栈地址
类型要求 必须标注 必须标注 可推断
放置位置 任意作用域 仅全局作用域 任意作用域
使用场景 编译时常量 全局共享数据 局部变量

4.5 静态变量(static)

基本用法

static GREETING: &str = "Hello, World!";
static MAX_RETRIES: u32 = 3;

fn main() {
    println!("{}", GREETING);
    println!("最大重试次数: {}", MAX_RETRIES);
}

可变静态变量(unsafe)

static mut COUNTER: u32 = 0;

fn increment() {
    // 访问可变静态变量必须在 unsafe 块中
    unsafe {
        COUNTER += 1;
        println!("计数器: {}", COUNTER);
    }
}

fn main() {
    increment();
    increment();
    increment();

    unsafe {
        println!("最终计数: {}", COUNTER); // 3
    }
}

注意: static mut 是不安全的,在多线程环境中可能导致数据竞争。推荐使用 AtomicU32Mutex<u32> 替代。

线程安全的静态变量

use std::sync::atomic::{AtomicU32, Ordering};

static COUNTER: AtomicU32 = AtomicU32::new(0);

fn increment() {
    COUNTER.fetch_add(1, Ordering::SeqCst);
}

fn main() {
    let handles: Vec<_> = (0..10)
        .map(|_| {
            std::thread::spawn(|| {
                for _ in 0..100 {
                    increment();
                }
            })
        })
        .collect();

    for handle in handles {
        handle.join().unwrap();
    }

    println!("计数器: {}", COUNTER.load(Ordering::SeqCst)); // 1000
}

4.6 类型推断

Rust 的类型推断

Rust 编译器可以根据上下文推断大部分变量类型:

fn main() {
    // 编译器推断为 i32(整数默认类型)
    let x = 42;

    // 编译器推断为 f64(浮点数默认类型)
    let y = 3.14;

    // 编译器根据使用推断类型
    let numbers: Vec<i32> = Vec::new();
    // 等价于:
    // let numbers = Vec::<i32>::new();

    // 从函数返回值推断
    let s = String::from("hello");
    let len = s.len(); // len 被推断为 usize

    println!("x={}, y={}, len={}", x, y, len);
}

需要显式标注类型的情况

fn main() {
    // 1. parse() 需要知道目标类型
    let n: i32 = "42".parse().unwrap();
    // 或使用 turbofish 语法
    let n = "42".parse::<i32>().unwrap();

    // 2. 数字字面量需要指定类型(默认 i32/f64 之外的类型)
    let a: u64 = 100;
    let b: i8 = -128;
    let c: f32 = 3.14;

    // 3. 集合为空时需要类型信息
    let mut v: Vec<i32> = Vec::new();
    v.push(1);

    // 4. 闭包返回类型需要明确
    let closure = |x: i32| -> i32 { x + 1 };

    println!("n={}, a={}, b={}, c={}", n, a, b, c);
    println!("v={:?}, closure(5)={}", v, closure(5));
}

4.7 数字字面量

整数字面量

格式 示例 说明
十进制 98_222 下划线分隔提高可读性
十六进制 0xff 0x 前缀
八进制 0o77 0o 前缀
二进制 0b1111_0000 0b 前缀
字节 b'A' 仅限 u8
fn main() {
    let decimal = 98_222;
    let hex = 0xff;
    let octal = 0o77;
    let binary = 0b1111_0000;
    let byte = b'A';

    println!("十进制: {}", decimal);   // 98222
    println!("十六进制: {}", hex);     // 255
    println!("八进制: {}", octal);     // 63
    println!("二进制: {}", binary);    // 240
    println!("字节: {}", byte);        // 65
}

浮点数字面量

fn main() {
    let x = 2.0;      // f64(默认)
    let y: f32 = 3.0;  // f32(显式标注)

    // 科学记数法
    let z = 1e6;        // 1000000.0 (f64)
    let w = 2.5e-3;     // 0.0025 (f64)

    println!("x={}, y={}, z={}, w={}", x, y, z, w);
}

数字类型一览

类型 大小 范围
i8 8 位 -128 到 127
i16 16 位 -32,768 到 32,767
i32 32 位 -2^31 到 2^31-1
i64 64 位 -2^63 到 2^63-1
i128 128 位 -2^127 到 2^127-1
isize 平台相关 平台指针大小
u8 8 位 0 到 255
u16 16 位 0 到 65,535
u32 32 位 0 到 2^32-1
u64 64 位 0 到 2^64-1
u128 128 位 0 到 2^128-1
usize 平台相关 平台指针大小
f32 32 位 IEEE 754 单精度
f64 64 位 IEEE 754 双精度

注意: 整数溢出在 debug 模式下会 panic,在 release 模式下会回绕(wrap around)。使用 checked_*wrapping_*saturating_*overflowing_* 方法明确处理溢出。


4.8 作用域与生命周期

作用域规则

fn main() {                       // main 作用域开始
    let x = 5;                    // x 绑定

    {                             // 内部作用域开始
        let y = 10;               // y 绑定
        println!("x + y = {}", x + y); // ✅ 可以访问外部的 x
    }                             // 内部作用域结束,y 被释放

    // println!("{}", y);         // ❌ 编译错误:y 不存在
    println!("x = {}", x);       // ✅ x 仍然有效
}                                 // main 作用域结束,x 被释放

遮蔽的作用域

fn main() {
    let x = 5;
    println!("x = {}", x); // 5

    {
        let x = x * 2; // 内部遮蔽,x = 10
        println!("内部 x = {}", x); // 10
    }

    // 外部 x 恢复
    println!("外部 x = {}", x); // 5
}

4.9 运算符

算术运算符

fn main() {
    let a = 10;
    let b = 3;

    println!("加法: {} + {} = {}", a, b, a + b);       // 13
    println!("减法: {} - {} = {}", a, b, a - b);       // 7
    println!("乘法: {} * {} = {}", a, b, a * b);       // 30
    println!("除法: {} / {} = {}", a, b, a / b);       // 3(整数除法)
    println!("取余: {} % {} = {}", a, b, a % b);       // 1

    // 浮点数运算
    let x = 10.0;
    let y = 3.0;
    println!("浮点除法: {} / {} = {}", x, y, x / y);   // 3.3333...

    // ⚠️ 整数除法会截断小数部分
    println!("7 / 2 = {}", 7 / 2);                      // 3,不是 3.5
}

比较与逻辑运算符

fn main() {
    // 比较运算符
    println!("1 == 1: {}", 1 == 1);     // true
    println!("1 != 2: {}", 1 != 2);     // true
    println!("1 < 2:  {}", 1 < 2);      // true
    println!("1 > 2:  {}", 1 > 2);      // false
    println!("1 <= 1: {}", 1 <= 1);     // true
    println!("1 >= 2: {}", 1 >= 2);     // false

    // 逻辑运算符
    let a = true;
    let b = false;
    println!("a && b: {}", a && b);     // false
    println!("a || b: {}", a || b);     // true
    println!("!a:     {}", !a);         // false

    // 位运算符
    let x: u8 = 0b1010;
    let y: u8 = 0b1100;
    println!("x & y:  {:04b}", x & y);  // 1000
    println!("x | y:  {:04b}", x | y);  // 1110
    println!("x ^ y:  {:04b}", x ^ y);  // 0110
    println!("!x:     {:04b}", !x);     // 11110101 (u8)
    println!("x << 1: {:04b}", x << 1); // 0100 (溢出位被丢弃)
    println!("x >> 1: {:04b}", x >> 1); // 0101
}

类型转换

Rust 不支持隐式类型转换,必须使用 as 关键字:

fn main() {
    let x: i32 = 42;
    let y: f64 = x as f64;    // i32 -> f64
    let z: u8 = x as u8;      // i32 -> u8(高位截断)

    println!("i32: {}", x);    // 42
    println!("f64: {}", y);    // 42.0
    println!("u8:  {}", z);    // 42

    // 浮点转整数会截断小数
    let f = 3.99;
    let i = f as i32;
    println!("3.99 as i32 = {}", i); // 3(不是四舍五入)

    // ⚠️ 超出目标类型范围时会回绕
    let big: u32 = 300;
    let small: u8 = big as u8;
    println!("300 as u8 = {}", small); // 44 (300 % 256)
}

4.10 业务场景示例

配置管理

const DEFAULT_PORT: u16 = 8080;
const DEFAULT_HOST: &str = "127.0.0.1";
const MAX_CONNECTIONS: usize = 1000;
const REQUEST_TIMEOUT_SECS: u64 = 30;

struct ServerConfig {
    host: String,
    port: u16,
    max_connections: usize,
    timeout_secs: u64,
}

impl ServerConfig {
    fn new() -> Self {
        Self {
            host: DEFAULT_HOST.to_string(),
            port: DEFAULT_PORT,
            max_connections: MAX_CONNECTIONS,
            timeout_secs: REQUEST_TIMEOUT_SECS,
        }
    }

    fn with_port(mut self, port: u16) -> Self {
        self.port = port;
        self
    }

    fn with_host(mut self, host: &str) -> Self {
        self.host = host.to_string();
        self
    }
}

fn main() {
    let config = ServerConfig::new()
        .with_port(3000)
        .with_host("0.0.0.0");

    println!("服务器配置:");
    println!("  地址: {}:{}", config.host, config.port);
    println!("  最大连接数: {}", config.max_connections);
    println!("  超时时间: {} 秒", config.timeout_secs);
}

单位转换工具

/// 温度转换
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
}

/// 字节单位转换
const KB: u64 = 1024;
const MB: u64 = KB * 1024;
const GB: u64 = MB * 1024;
const TB: u64 = GB * 1024;

fn format_bytes(bytes: u64) -> String {
    if bytes >= TB {
        format!("{:.2} TB", bytes as f64 / TB as f64)
    } else if bytes >= GB {
        format!("{:.2} GB", bytes as f64 / GB as f64)
    } else if bytes >= MB {
        format!("{:.2} MB", bytes as f64 / MB as f64)
    } else if bytes >= KB {
        format!("{:.2} KB", bytes as f64 / KB as f64)
    } else {
        format!("{} B", bytes)
    }
}

fn main() {
    // 温度转换
    let c = 36.5;
    let f = celsius_to_fahrenheit(c);
    println!("{:.1}°C = {:.1}°F", c, f);

    let f = 98.6;
    let c = fahrenheit_to_celsius(f);
    println!("{:.1}°F = {:.1}°C", f, c);

    // 字节格式化
    let sizes = vec![500, 1536, 1048576, 1073741824, 1099511627776];
    for size in sizes {
        println!("{} bytes = {}", size, format_bytes(size));
    }
}

4.11 本章小结

要点 说明
默认不可变 let 声明的变量默认不可变,需 mut 显式声明可变
遮蔽 let 重新声明同名变量,可以改变类型和可变性
const 编译时常量,必须标注类型,使用 SCREAMING_SNAKE_CASE
static 全局静态变量,有固定地址,static mut 需 unsafe
类型推断 大部分情况编译器可推断,但 parse() 等需要额外信息
整数溢出 debug 模式 panic,release 模式回绕
类型转换 使用 as 显式转换,无隐式转换

扩展阅读

  1. Rust 基本类型 — 官方教程
  2. Rust 语言参考 - 类型 — 类型系统参考
  3. num_traits crate — 数值类型 trait 集合
  4. Rust 整数溢出处理 — 溢出行为详解