一、变量的特性

1.可变性(Mutability)

默认不可变(Immutable by Default) 这是Rust中的一个核型设计理念。当声明
一个变量时,默认是不可变,一旦赋值不可修改。

let x = 5; //x 是不可变的
x = 6; //这行代码会报错

使用mut关键字声明可变变量, 如要修改必须先声明mut关键字。

let mut y = 10; //y是可变的
y = 20; //这行代码是允许的
println!("y 的值是:{}",y); //输出:y 的值是:20

默认不可变有助于编写安全易于理解的和并发的代码,防止意外改变量的值,减少bug产生()。

2.变量声明(Variable Declaration)

使用let关键字来声明一个变量。

let message = "Hello,world!";
let connect = 0;
let is_active = true;

3.类型注释(Type Annotations)

Rust属于静态类型语言,这意味着编译去需要在编译时知道所有变量的类型。
Rust拥有强大的类型推断(type inference) 能力,很多时候编译器可以根据你赋予的变量的值自动推断出其类型,因此不需要显示地指定类型。

let number = 30; //编译器推断 number 是 i32 类型
let pi = 3.14159; //编译器推断 pi 是 f64 类型

也可以在声明变量前显示地指定其类型,这被称为类型注解。类型主页在变量名后面使用,然后是类型名称

let age: u32 = 25;
let name: String = String::from("Alice");
let price: f32 = 99.99;

显示类型注解在一下情况很有用:

  • 编译器无法推断出变量的类型
  • 你希望变量拥有特定的类型
  • 提高代码可读性

4.变量作用域(Variable Scope)

变量的作用域绝地了变量在程序的那些部分是可见和可访问的。Rust使用块作用域(blicj scope) 变量只在其被声明的块(由花括号包裹的代码区域)及其子块中有效。

fn main(){
    let outer_variable = 10;
    {
        let inner_variable = 20;
        println!("内部块的变量:{}",inner_variable);//可以访问
        println!("外部块的变量:{}",outer_variable);//可以访问
    }
    println!("外部块的变量:{}",outer_variable);//可以访问
    // println!("内部变量块:{}",inner_variable);//错误! inner_variable 超出作用域
}

5.变量遮蔽(Variable Shadowing)

Rust允许声明一个与现有变量同名的新变量。这被称为变量覆盖。新的变量会“遮蔽”掉旧的变量,这意味着在新的作用域中,你访问的是新的变量。

let x = 5;
println!("第一个 x 的值:{}",x);//输出:第一个 x 的值:5
let x = x + 1;
println!("遮蔽后的 x 的值:{}",x);//输出:遮蔽后的 x 的值:6
{
    let x = x * 2;
    println!("内部块中遮蔽后的 x 的值:{}",x);//输出:内部块中的遮蔽后的 x 的值:12
}
println!("离开内部块后 x 的值:{}",x);//输出:离开内部块后 x 的值:6(仍然是外部作用域遮蔽后的结果)

变量遮蔽很有用,因为它允许你重用变量名,而无需担心引入新的变量名,并且可以方便地修改变量的类型(虽然通常不推荐这样做,因为它可能降低代码的可读性)。

6.变量类型(Variable Types)

  • 标量类型(Scalar Types):表示单个值

    • 整型(Integers):

    • 有符号:

      • i8:8 位有符号整数,范围从 -128 到 127。
      • i16:16 位有符号整数,范围从 -32,768 到 32,767。
      • i32:32 位有符号整数,范围从 -2,147,483,648 到 2,147,483,647。这是 Rust 的默认整型类型
      • i64:64 位有符号整数,范围很大。
      • i128:128 位有符号整数,范围非常大。
    • 无符号:

      • u8:8 位无符号整数,范围从 0 到 255。
      • u16:16 位无符号整数,范围从 0 到 65,535。
      • u32:32 位无符号整数,范围从 0 到 4,294,967,295。
      • u64:64 位无符号整数,范围很大。
      • u128:128 位无符号整数,范围非常大。
    • 指针大小:

      • isize:有符号,大小与指针相同。常用于索引数组或集合。
      • usize:无符号,大小与指针相同。常用于索引数组或集合,表示内存大小。

    > 选择整型类型的考量: 根据你需要表示的数值范围以及是否需要负数来选择合适的整型类型。i32 通常是性能和范围的良好折衷。
    >

    • 浮点型(Floating-Point Number):用于表示带有小数部分的数字。Rust 有两种主要的浮点类型:

    • f32:32 位浮点数 (单精度)。

    • f64:64 位浮点数 (双精度)。这是 Rust 的默认浮点类型,因为它在精度和性能之间提供了更好的平衡。

    > 浮点数在计算机中以近似值存储,可能会导致一些精度问题。
    >

    • 布尔类型(Booleans):用于表示真或假的值

    • bool:只能有两个可能的值:true (真) 或 false (假)。布尔值通常用于条件判断。

    • 字符型(Characters) : 用于表示单个 Unicode 标量值。

    • char: 可以表示字母、数字、符号以及 Unicode 定义的各种字符(包括中文、日文、韩文、表情符号等)。char 类型在内存中占用 4 个字节。

  • 复合类型(Compound Types):可以将多个值组合成一个类型

    • 元组(Tuples) :可以将固定数量的不同类型的值组合在一起。元组的长度一旦确定就不能改变。

    • let my_tuple: (i32, f64, char) = (500, 6.4, 'a');
      let (x, y, z) = my_tuple;//结构元组
      println!("y 的值是:{}",y);//输出:y 的值是:6.4
      println!("元组的第一个元素:{}",my_tuple.0);//通过索引访问
    • > 单元元组(),只有一个元素的元组需要加逗号(5,)以区分与括号表达式。
      >

    • 数组(Arrays) :可以将固定数量的相同类型的值组合在一起。数组的长度在编译时就必须确定,并且长度不可。变数组中的元素在内存中是连续存储的。

    • let my_array: [i32; 5] = [1, 2, 3, 4, 5]; //类型是[i32; 5], 表示包含 5 个 i32 类型的元素
      let another_array = [0; 10]; //创建一个包含 10 个值为 0 的 i32 数组
      println!("数组的第一个元素:{}", my_array[0]); //通过索引访问
    • 切片(Slices) :是对数组或String等数据结构中一段连续元素的引用。切片本身不拥有数据他们只是借用了底层数据的一部分,切片的长度在运行时可以改变。

    • let array = [1, 2, 3, 4, 5];
      let slice: &[i32] = &array[1..3]; //slice引用了array中索引 1 和 2 的元素(2,3)
      println!("切片的长度:{}", slice.len()); //输出:2
      let s = String::from("hello world");
      let hello = &s[0..5]; //String slice
      println!("字符串切片:{}", hello);
    • > String: 这是 Rust 标准库中提供的可增长的、可变的、拥有所有权的字符串类型。它在堆上分配内存,可以动态地改变大小。
      >

  • 指针类型(Pointer Types):引用内存中的某个位置

    • 不可变引用 &T: 允许你引用数据但不允许修改它。可以有多个不可变引用指向同一块数据。

    • let immutable_value = 10;
      let ref_immutable = &immutable_value; // 不可变引用
    • 可变引用 &mut T: 允许你修改被引用的数据。在特定的作用域内,对于同一块数据,只能有一个可变引用。

    • let mut mutable_value = 20;
      let ref_mutable = &mut mutable_value; // 可变引用
      *ref_mutable = 30; // 通过可变引用修改值
      println!("mutable_value: {}", mutable_value); // 输出: mutable_value: 30
    • > 引用是 Rust 安全内存管理的核心,编译器会强制执行借用规则,防止悬垂引用和数据竞争。
      >

    • 裸指针 (Raw Pointers) *const T (指向 T 类型的不可变裸指针) 和 *mut T (指向 T 类型的可变裸指针)。裸指针类似于 C 和 C++ 中的指针,不提供 Rust 的安全保证。它们可以为空 (null),并且编译器不会强制执行生命周期或所有权规则。裸指针通常只在与不安全代码 (unsafe 块) 交互时使用,例如调用 C 库或进行底层内存操作。

  • 特殊类型

    • 单元类型 (Unit Type)()

    • 表示空值或没有返回值。它只有一个可能的值,也写作 ()

    • 函数如果没有任何显式的返回值,则隐式地返回单元类型。

    • 有时也用作占位符或表示某个操作的完成。

    • fn do_something() {} // 这个函数隐式返回 ()
      let unit_value = ();
    • 发散类型 (Diverging Type)!

    • 表示一个永远不会返回的函数。这种函数通常会 panic (程序崩溃) 或无限循环。

    • 例如,标准库中的 panic! 宏的返回类型就是 !

    • fn never_returns() -> ! {
        panic!("我永远不会返回!");
      }

7.变量初始化 (Variable Initialization)

  • Rust 要求在使用变量之前必须对其进行初始化 (initialize) ,即赋予一个初始值。如果你声明了一个变量但没有赋初始值就尝试使用它,编译器会报错。
  • let a: i32;
    // println!("a 的值是: {}", a); // 错误!a 未被初始化
    a = 10;
    println!("a 的值是: {}", a); // 现在可以正常使用

Rust 变量的关键点

  • 默认不可变,使用 mut 声明可变变量。

  • 使用 let 关键字声明变量。

  • 可以进行类型推断,也可以显式地使用类型注解。

  • 拥有块作用域。

  • 支持变量遮蔽。

  • 拥有丰富的内置数据类型。

  • 必须在使用前进行初始化。