变量绑定(Variable Bindings)

一、变量绑定 Variable Bindings

Rust 通过静态类型保证类型安全。声明变量绑定时可以手动标注类型;但绝大多数场景编译器能从上下文自动类型推导,不用手写类型注解。

使用 let 将值(字面量等)绑定到变量。

fn main() {
    let an_integer = 1u32;        // u32 无符号32位整数
    let a_boolean = true;          // bool 布尔值
    let unit = ();                 // 单元类型 (),类似其他语言void

    // 将 an_integer 的值拷贝给 copied_integer
    let copied_integer = an_integer;

    println!("An integer: {}", copied_integer);
    println!("A boolean: {}", a_boolean);
    println!("Meet the unit value: {:?}", unit); // {:?} 调试打印

    // Rust 编译器会警告未使用的变量;变量名以下划线开头可以消除警告
    let _unused_variable = 3u32;

    let noisy_unused_variable = 2u32; 
    // 警告:未使用变量,改成 _noisy_unused_variable 消除警告
}Code language: JavaScript (javascript)

执行结果

An integer: 1
A boolean: true
Meet the unit value: ()Code language: JavaScript (javascript)

知识点:_变量名 告诉编译器“我故意不使用这个变量”,屏蔽 unused 警告;单纯下划线 _ 是丢弃值,和 _var 有区别。

二、可变性 Mutability

默认所有变量绑定都是不可变(immutable),想要修改变量必须加上 mut 修饰符。

fn main() {
    let _immutable_binding = 1;      // 不可变变量
    let mut mutable_binding = 1;     // 可变变量

    println!("Before mutation: {}", mutable_binding);

    mutable_binding += 1; // 合法,可变变量允许修改
    println!("After mutation: {}", mutable_binding);

    _immutable_binding += 1; 
    // 编译报错!不能修改不可变绑定
}Code language: JavaScript (javascript)

编译器会输出清晰的错误信息,提示你尝试修改不可变变量。

三、作用域与变量遮蔽 Scope and Shadowing

变量绑定存在作用域,只存活在代码块 {} 内部。代码块是一对大括号包裹的语句集合。

fn main() {
    // 存活于整个main函数
    let long_lived_binding = 1;

    // 内部代码块,更小的作用域
    {
        // 仅在当前块内有效
        let short_lived_binding = 2;
        println!("inner short: {}", short_lived_binding);
    }
    // 代码块结束,short_lived_binding 销毁

    // 报错:外部作用域访问不到块内变量,读者可以打开注释观察
    //println!("outer short: {}", short_lived_binding);
    	
    println!("outer long: {}", long_lived_binding);
}Code language: JavaScript (javascript)

变量遮蔽(Shadowing)

允许在内部作用域用同名 let 新建变量,遮蔽外层同名变量,二者是完全独立的绑定,不代表修改外层变量。

在其它的语言中,像下面这样,在内部代码块中如果出现同名的变量,会直接无法编译,例如C#

fn main() {
    let shadowed_binding = 1;

    {
        println!("before being shadowed: {}", shadowed_binding);

        // 新建同名变量,遮蔽外层
        let shadowed_binding = "abc";
        println!("shadowed in inner block: {}", shadowed_binding);
    }

    // 离开内部块,内部遮蔽失效,恢复外层变量
    println!("outside inner block: {}", shadowed_binding);

    // 在外部作用域再次遮蔽
    let shadowed_binding = 2;
    println!("shadowed in outer block: {}", shadowed_binding);
}Code language: JavaScript (javascript)

输出

inner short: 2
outer long: 1

  • let x = ... 新建遮蔽,可以更换类型;
  • mut x 赋值修改 → 原地修改,类型不能变。

四、先声明、后初始化 Declare first

可以先声明变量,之后再赋值初始化;但是变量在使用前必须完成初始化
Rust 禁止读取未初始化变量,防止未定义行为。

工程上不推荐声明和初始化隔很远,可读性差;尽量靠近使用位置定义变量。

fn main() {
    // 仅声明绑定,暂不赋值
    let a_binding;

    {
        let x = 2;
        a_binding = x * x; // 在块内完成初始化
    }
    println!("a binding: {}", a_binding); //输出:4

    let another_binding;
    //println!("another binding: {}", another_binding); // 报错:使用未初始化变量,取消注释此行 观察

    
    another_binding = 1;
    println!("another binding: {}", another_binding); // 输出:1
}Code language: JavaScript (javascript)

取消上面的注销后,会报错误

五、冻结 Freezing

当可变变量被同名不可变变量遮蔽时,原数据会被「冻结」:
在这个作用域内无法修改,直到该不可变绑定走出作用域销毁。

fn main() {
    let mut _mutable_integer = 7i32;

    {
        // 使用let遮蔽,生成不可变绑定,冻结原有数据
        let _mutable_integer = _mutable_integer;

        _mutable_integer = 50; 
        // 报错!当前作用域变量不可变,无法修改;注释此行
        // 内部块结束,这个遮蔽绑定销毁
    }

    // 冻结解除,可以正常修改
    _mutable_integer = 3;
}Code language: JavaScript (javascript)

总结

  1. let 创建变量绑定,默认不可变let mut 可变;
  2. 类型大多自动推导,可手动标注 let x: u32 = 10;
  3. 变量受作用域限制,{} 创造局部范围;
  4. Shadowing(遮蔽)let 同名变量 创建新变量,可换类型;和 mut 修改完全不同;
  5. 变量必须初始化才能读取,禁止未初始化值;
  6. 可变变量被同名不可变遮蔽 → 冻结,暂时不能修改;
  7. _变量名 消除未使用警告;

Previous:

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注