rust学习笔记之基础:变量及可变性、常量

变量和可变性

变量绑定

Rust 通过静态类型确保类型安全。变量绑定可以在声明时说明类型,不过在多数情况下,编译器能够从上下文推导出变量的类型,从而大大减少了类型说明的工作。

使用 let 绑定操作可以将值(比如字面量)绑定(bind)到变量。

fn main() {
    let an_integer = 1u32;
    let a_boolean = true;
    let unit = ();
    let copied_integer = an_integer;

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

可变变量

变量绑定默认是不可变的(immutable),当尝试改变一个前面指定为不可变的值时会得到编译错误。但加上 mut 修饰语后变量就可以改变。

fn main() {
    let x = 5; //不可变
    let mut y = 1; //可变
}

作用域和遮蔽

变量绑定有一个作用域(scope),它被限定只在一个代码块(block)中生存(live)。 代码块是一个被 {} 包围的语句集合。

另外也允许变量遮蔽(variable shadowing)。可以声明和前面变量具有相同名称的新变量,称这种行为是第一个变量被第二个变量遮蔽(shadow),这意味着当我们使用变量时我们看到的会是第二个变量的值。

fn main() {
    // 此绑定生存于 main 函数中
    let long_lived_binding = 1;

    // 这是一个代码块,比 main 函数拥有更小的作用域
    {
        // 此绑定只存在于本代码块
        let short_lived_binding = 2;
        println!("inner short: {}", short_lived_binding);

        // 此绑定*遮蔽*了外面的绑定
        let long_lived_binding = 5_f32;
        println!("inner long: {}", long_lived_binding);
    }
    // 代码块结束

    // 报错!`short_lived_binding` 在此作用域上不存在
    // println!("outer short: {}", short_lived_binding);

    println!("outer long: {}", long_lived_binding);

    // 此绑定同样*遮蔽*了前面的绑定
    let long_lived_binding = 'a';
    println!("outer long: {}", long_lived_binding);
}

输出

inner short: 2
inner long: 5
outer long: 1
outer long: a

变量遮蔽和将变量标记为 mut 的方式的区别:

  • 除非我们再次使用 let 关键字,否则若是我们不小心尝试重新赋值给这个变量,我们将得到一个编译错误。通过使用 let,我们可以对一个值进行一些转换,但在这些转换完成后,变量将是不可变的。
  • mut 和变量遮蔽之间的另一个区别是,因为我们在再次使用 let 关键字时有效地创建了一个新的变量,所以我们可以改变值的类型,但重复使用相同的名称。

变量先声明

可以先声明(declare)变量绑定,后面才将它们初始化(initialize)。但是这种做法很 少用,因为这样可能导致使用未初始化的变量。

fn main() {
    let a_binding;// 声明一个变量绑定

    {
        let x = 2;

        a_binding = x * x;// 初始化一个绑定
    }

    println!("a binding: {}", a_binding);

    let another_binding;

    // 报错!使用了未初始化的绑定
    // println!("another binding: {}", another_binding);

    another_binding = 1;
    println!("another binding: {}", another_binding);
}

输出

a binding: 4
another binding: 1

编译器禁止使用未经初始化的变量,因为这会产生未定义行为(undefined behavior)。

冻结

当数据被相同的名称不变地绑定时,它还会冻结(freeze)。在不可变绑定超出作用域之前,无法修改已冻结的数据:

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

    {
        let _mutable_integer = _mutable_integer;// 被不可变的 `_mutable_integer` 遮蔽
        // _mutable_integer = 50;// 报错!`_mutable_integer` 在本作用域被冻结
        // `_mutable_integer` 离开作用域
    }

    // 正常运行! `_mutable_integer` 在这个作用域没有冻结
    _mutable_integer = 3;
}

常量

常量(constant)是绑定到一个常量名且不允许更改的值。常量不仅仅默认不可变,而且自始至终不可变。常量常常使用 const 关键字来声明,并且值的类型必须注明。Rust 常量的命名约定是全部字母都使用大写,并使用下划线分隔单词。

Rust 有两种常量,可以在任意作用域声明,包括全局作用域。它们都需要显式的类型声明:

  • const:不可改变的值(通常使用这种)。
  • static:具有 'static 生命周期的,可以是可变的变量(须使用 static mut 关键字)。

有个特例就是 "string" 字面量。它可以不经改动就被赋给一个 static 变量,因为它的类型标记:&'static str 就包含了所要求的生命周期 'static。其他的引用类型都必须特地声明,使之拥有'static 生命周期。这两种引用类型的差异似乎也无关紧要,因为无论如何,static 变量都得显式地声明。

// 全局变量是在在所有其他作用域之外声明的。
static LANGUAGE: &'static str = "Rust";
const THRESHOLD: i32 = 10;
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

fn is_big(n: i32) -> bool {
    // 在一般函数中访问常量
    n > THRESHOLD
}

fn main() {
    let n = 16;

    println!("This is {}", LANGUAGE);
    println!("The threshold is {}", THRESHOLD);
    println!("{} is {}", n, if is_big(n) { "big" } else { "small" });
}
posted @ 2025-07-21 10:09  carol2014  阅读(23)  评论(0)    收藏  举报