rust的变量、数据类型、函数、控制流


  1. 变量

在 Rust 中,使用 let 关键字来声明变量。

核心特性:变量默认不可变

fn main() {
    let x = 5; // 声明一个不可变变量 x,其值为 5
    println!("The value of x is: {}", x);
    x = 6; // 这行代码会编译错误!Cannot assign twice to immutable variable `x`
    println!("The value of x is: {}", x);
}

这是 Rust 安全性和并发性设计的基石。它防止了值被意外改变。如果你希望一个变量是可变的,必须显式地使用 mut 关键字。

fn main() {
    let mut x = 5; // 声明一个可变变量 x
    println!("The value of x is: {}", x);
    x = 6; // 这是允许的,因为 x 是可变的
    println!("The value of x is: {}", x);
}

最佳实践:优先使用不可变变量,除非你明确需要改变其值。这能使代码更清晰、更安全。

变量隐藏(Shadowing)

你可以使用与之前变量相同的名字来声明一个新的变量。这被称为“隐藏”,意味着第二个变量“覆盖”了第一个。

fn main() {
    let x = 5; // 第一个 x,不可变

    let x = x + 1; // 第二个 x,隐藏了第一个 x。它仍然是不可变的,但值变成了 6。

    {
        let x = x * 2; // 第三个 x,在这个代码块内隐藏了第二个 x。值变成了 12。
        println!("The value of x in the inner scope is: {}", x); // 输出 12
    } // 代码块结束,第三个 x 离开作用域

    println!("The value of x is: {}", x); // 输出 6,第二个 x 重新可见
}

隐藏 vs. mut:

· 隐藏:实际上是创建了一个新变量。你可以改变值的类型(例如从数字变成字符串),但新变量依然是不可变的,除非你用 mut。
· mut:是同一个变量,值可以改变,但类型不能改变。

let spaces = "   ";    // spaces 是字符串类型
let spaces = spaces.len(); // 隐藏后,spaces 是数字类型。如果用 `mut` 则无法这样操作。

  1. 数据类型

Rust 是静态类型语言,这意味着在编译时就必须知道所有变量的类型。编译器通常能根据值和使用方式推断出类型,但有时也需要显式注解。

数据类型分为两大类:标量和复合。

标量类型

代表单个值。

  1. 整型:
    · 有符号:i8, i16, i32, i64, i128, isize(i 开头)
    · 无符号:u8, u16, u32, u64, u128, usize(u 开头)
    · 数字大小:8, 16, 32, 64, 128 表示位数。isize 和 usize 取决于系统架构(32位或64位)。
    · 默认是 i32。
    · 字面值:98_222(十进制),0xff(十六进制),0o77(八进制),0b1111_0000(二进制),b'A'(字节,仅限 u8)。
  2. 浮点型:
    · f32(单精度)和 f64(双精度)
    · 默认是 f64,因为在现代 CPU 上速度与 f32 差不多但精度更高。
    · 字面值:3.14, -10.0, 2.,2.0f32
  3. 布尔型:
    · true 和 false
    · 类型注解是 bool
  4. 字符类型:
    · 使用单引号 '' 声明,占用 4 个字节。
    · 代表一个 Unicode 标量值,这意味着它可以表示比 ASCII 多得多的字符:中文、日文、韩文、emoji 等。
    · 示例:let c = 'z'; let z = 'ℤ'; let heart_eyed_cat = '😻';

复合类型

将多个值组合成一个类型。

数组:
· 数组中的每个元素必须具有相同的类型。
· 数组的长度是固定的(与 Vector 不同,Vector 是标准库提供的可变长度集合)。
· 使用方括号 [] 创建。
· 当你希望数据分配在栈上而不是堆上时,或者当你想要确保总是有固定数量的元素时,数组很有用。

fn main() {
    // 声明一个数组,类型注解 [i32; 5] 表示元素类型为 i32,长度为 5
    let a: [i32; 5] = [1, 2, 3, 4, 5];

    // 另一种声明方式:创建一个每个元素值都为 3 的数组,长度为 5
    let b = [3; 5]; // 等同于 let b = [3, 3, 3, 3, 3];

    // 访问数组元素
    let first = a[0]; // 1
    let second = a[1]; // 2

    // 如果访问 a[5],编译会通过,但运行时会 panic(程序因错误而退出)!
    // 这是 Rust 内存安全原则的一个关键体现:数组访问会在运行时进行边界检查。
}

元组:
· 将多个不同类型的值组合在一起。长度固定,一旦声明,其长度和类型就不能改变。
· 使用圆括号 () 创建。
· 通过模式匹配解构(destructuring)或使用点号 . 后跟索引来访问元素。

fn main() {
    // 声明一个元组,类型注解 (i32, f64, u8)
    let tup: (i32, f64, u8) = (500, 6.4, 1);

    // 解构
    let (x, y, z) = tup;
    println!("The value of y is: {}", y); // 输出 6.4

    // 通过索引访问
    let five_hundred = tup.0; // 500
    let six_point_four = tup.1; // 6.4
    let one = tup.2; // 1
}

  1. 函数

使用 fn 关键字来声明函数。Rust 使用 snake case 规范作为函数和变量名的命名风格:所有字母都是小写,并使用下划线分隔单词。

参数

函数参数必须显式声明类型。这是函数签名的一部分,编译器需要它来理解你的意图。

fn another_function(x: i32, y: f64) { // x 是 i32 类型,y 是 f64 类型
    println!("The value of x is: {}", x);
    println!("The value of y is: {}", y);
}

语句与表达式

· 语句:执行操作但不返回值的指令。let x = 6; 是一个语句。
· 表达式:会计算并产生一个值。5 + 6 是一个表达式,值为 11。函数调用、宏调用、一个单独的 {} 代码块都是表达式。

fn main() {
    let y = {         // 这个代码块是一个表达式...
        let x = 3;
        x + 1         // 注意:这里没有分号!加上分号就变成了语句,不再返回值。
    };                // ...它的值是 4,并赋值给 y

    println!("The value of y is: {}", y); // 输出 4
}

返回值

在 -> 后声明函数返回值的类型。在 Rust 中,函数的返回值等同于函数体最后一个表达式的值。

可以使用 return 关键字提前返回,但大多数函数都隐式地返回最后的表达式。

fn five() -> i32 { // 声明返回类型为 i32
    5 // 没有分号,这是一个表达式,其值 5 被返回
}

fn plus_one(x: i32) -> i32 {
    x + 1 // 返回值
    // x + 1; // 如果加上分号,就变成了语句,返回 `()`(单元类型),与声明的 -> i32 冲突,会导致编译错误。
}

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

  1. 控制流

if 表达式

if 条件必须是 bool 类型。Rust 不会自动将非布尔值转换为布尔值。

fn main() {
    let number = 3;

    if number < 5 { // 条件必须是 bool
        println!("condition was true");
    } else if number % 2 == 0 {
        println!("number is divisible by 2");
    } else {
        println!("condition was false");
    }
}

因为 if 是一个表达式,所以可以在 let 语句的右侧使用它。

fn main() {
    let condition = true;
    let number = if condition { 5 } else { 6 }; // 两个分支必须返回相同类型!
    // let number = if condition { 5 } else { "six" }; // 错误!类型不匹配

    println!("The value of number is: {}", number); // 5
}

循环

Rust 提供了三种循环:loop、while 和 for。

for:遍历集合。最常用、最安全、最简洁的循环方式。它利用了“迭代器”的概念,避免了 while 循环中可能出现的索引越界错误。

fn main() {
    let a = [10, 20, 30, 40, 50];

    // 使用 for 循环遍历数组元素
    for element in a.iter() { // .iter() 返回一个迭代器
        println!("the value is: {}", element);
    }

    // 更常见的用法:使用范围 (Range)
    for number in (1..4).rev() { // (1..4) 生成 1,2,3。.rev() 将其反转
        println!("{}!", number);
    }
    // 输出:
    // 3!
    // 2!
    // 1!
}

while:条件循环,每次循环前都判断条件。

fn main() {
    let mut number = 3;

    while number != 0 {
        println!("{}!", number);
        number -= 1;
    }

    println!("LIFTOFF!!!");
}

loop:无限循环,直到显式要求停止(break)。

fn main() {
    let mut counter = 0;

    let result = loop { // loop 也是一个表达式,可以从 break 返回值
        counter += 1;
        if counter == 10 {
            break counter * 2; // 跳出循环并返回 counter * 2
        }
    };

    println!("The result is {}", result); // 20
}

Read more

Rust 结构体、枚举、模式匹配、Option 和 Result、包和模块

1. 结构体(Structs)深度解析 什么是结构体? 结构体是一种自定义数据类型,允许你将多个相关的值组合在一起,形成一个有意义的组合。 定义和使用结构体 // 1. 基本结构体定义 struct User { username: String, // 字段:用户名 email: String, // 字段:邮箱 sign_in_count: u64, // 字段:登录次数 active: bool, // 字段:是否活跃 } // 2. 创建结构体实例 fn create_user() { // 不可变实例 let user1 = User { email: String::from("alice@example.com"), username: String:

By amm

Rust 生命周期

1. 什么是生命周期? 生命周期是引用有效的作用域范围。在 Rust 中,每个引用都有一个生命周期,这是它保持有效的作用域。 fn main() { // 生命周期开始 let x = 5; // x 的生命周期开始 let r = &x; // r 的生命周期开始,引用 x println!("r = {}", r); // 生命周期结束 } // x 和 r 的生命周期结束 2. 为什么需要生命周期? 问题:悬垂引用(Dangling References) // 这个代码无法编译! fn main() { let reference; { let value = 42; // value 在内部作用域创建

By amm

rust所有权、借用、生命周期

1. 所有权 所有权是 Rust 最独特的特性,它让 Rust 无需垃圾回收即可保证内存安全。 所有权规则 1. Rust 中的每一个值都有一个被称为其所有者的变量。 2. 值在任一时刻有且只有一个所有者。 3. 当所有者(变量)离开作用域,这个值将被丢弃。 变量与数据交互的方式 移动 fn main() { let s1 = String::from("hello"); // s1 拥有字符串 "hello" let s2 = s1; // s1 的所有权被移动(move)到 s2 // println!("{}, world!", s1); // 这行会编译错误!s1

By amm

© 2025 路不易All rights reserved.

黔ICP备2025043243号-1 | 公安备案图标 贵公网安备52052402000220号