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

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
关键字letlet 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

特性conststaticlet
可变性不可变不可变(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下划线分隔提高可读性
十六进制0xff0x 前缀
八进制0o770o 前缀
二进制0b1111_00000b 前缀
字节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);
}

数字类型一览

类型大小范围
i88 位-128 到 127
i1616 位-32,768 到 32,767
i3232 位-2^31 到 2^31-1
i6464 位-2^63 到 2^63-1
i128128 位-2^127 到 2^127-1
isize平台相关平台指针大小
u88 位0 到 255
u1616 位0 到 65,535
u3232 位0 到 2^32-1
u6464 位0 到 2^64-1
u128128 位0 到 2^128-1
usize平台相关平台指针大小
f3232 位IEEE 754 单精度
f6464 位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 整数溢出处理 — 溢出行为详解