【Rust】基本概念

news2025/4/24 5:38:28

目录

  • 第一个 Rust 程序:猜数字
  • 基本概念
    • 变量和可变性
      • 可变性
      • 常量
      • 变量隐藏
    • 数据类型
      • 标量类型
        • 整型
        • 浮点型
        • 数值运算
        • 布尔型
        • 字符类型
      • 复合类型
        • 元组
        • 数组
    • 函数
      • 参数
      • 语句与表达式
      • 函数返回值
    • 控制流
      • 使用 if 表达式控制条件
        • if 表达式
        • 使用 else if 处理多重条件
        • 在 let 语句中使用 if
      • 使用循环重复执行
        • 使用 loop 重复执行代码
        • 从循环返回值
        • 循环标签
        • while 条件循环
        • 使用 for 遍历集合

第一个 Rust 程序:猜数字

正式开始学习 Rust 前,先来看一下下面的代码,其中有注释,用于大致了解 Rust 中的基本概念和用法,看不懂也没关系,后面会慢慢讲到。

猜数字小游戏:

use std::io; //引入标准库std中的io库
use rand::Rng; //引入第三方库rand中的Rng trait,类似于Java中的接口或者抽象类
//也可以用use rand::prelude::*;
use std::cmp::Ordering; //引入标准库std中的cmp模块中的Ordering类型

fn main() {
  	//println!是宏(不是函数),用于打印到标准输出(默认带换行)
    println!("Guess the number!");
	
  	//生成1-100的随机数
  	/*
  	*可以理解成rand中的thread_rng()返回了一个ThreadRng类型的随机数生成器
  	*并且ThreadRng实现了Rng trait的gen_range()方法
  	*所以该生成器调用了rand库中Rng trait中的gen_range()方法来生成指定范围内的随机数
  	*/
  	/*
  	*rand::thread_rng() 返回一个 ThreadRng 类型的随机数生成器
  	*该类型实现了 Rng trait,而 gen_range() 就是定义在 Rng trait 中的方法
  	*因此,我们可以通过 ThreadRng 调用 gen_range() 来生成指定范围内的随机数
  	*/
    let secret_number = rand::thread_rng().gen_range(1..=100); //1-100的随机数
  
  	// "secret_number: {}"是输出模板,{} 是占位符,表示要插入一个变量的值
    println!("secret_number: {}",secret_number);

    loop{
        println!("Please input your guess.");
				
      	//调用 String 类型的关联函数 new(),创建一个空的 String 实例。
        let mut guess = String::new();
      
      	//读取用户输入数据
      	//表示从标准输入中获取一个输入句柄,等价于 std::io::stdin()
      	//返回一个 Stdin 类型的对象,用于读取用户输入
        io::stdin()
      			//read_line 方法会等待用户输入一行内容并按下 Enter
            //它会把输入的内容追加到 guess 字符串里(包括换行符 \n)
            //参数是 &mut guess,所以 guess 必须是一个可变的 String
            //返回一个 Result<usize>,表示读取了多少个字节,或者发生了错误
            .read_line(&mut guess)
      			//expect 是 Result 类型的方法。
            //如果 read_line 成功,程序继续执行。
            //如果失败(例如输入设备出错),就会打印 "Failed to read line",然后程序崩溃退出(panic)
            .expect("Failed to read line");
      	
      	//也能用match来替代expect
      	/*
      	match io::stdin().read_line(&mut guess) {
            Ok(_) => {},
            Err(_) => {
                println!("Failed to read line");
                return;
            }
        };
      	*/
      
        println!("You guessed: {}",guess);

      	//match匹配 parse() 返回的结果,是 Result 类型
      	//.trim() 去除前后空白字符,返回 &str
        let guess: u32 = match guess.trim().parse(){
            Ok(num) => num,//如果成功解析,num 就是那个转换后的 u32 数字,把它赋值给前面的 let guess: u32
            Err(_) => continue,//如果解析失败,比如你输入了字母,程序就跳过当前循环,continue 重新来过
        };

      	//这是 cmp() 方法,来自 PartialOrd/Ord trait(u32 实现了它)
      	//返回值是一个 Ordering 枚举类型
        match guess.cmp(&secret_number) {
            Ordering::Less => println!("Too small!"),
            Ordering::Equal => {println!("You win!");break;},
            Ordering::Greater => println!("Too big!"),
        }
    }
}

大致了解完了吗,下面将开启 Rust 之旅!

基本概念

变量和可变性

可变性

在 Rust 中变量默认是不可改变的(immutable)。

当变量不可变时,一旦变量被赋予了某个值,这个值就不能被改变。代码如下:

fn main() {
    let x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}

运行上述代码将得到一条与不可变性有关的错误信息:

error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:4:5
  |
2 |     let x = 5;
  |         - first assignment to `x`
3 |     println!("The value of x is: {x}");
4 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable
  |
help: consider making this binding mutable
  |
2 |     let mut x = 5;
  |         +++

这是由于尝试修改了变量 x 的值导致的编译时错误。尽管变量默认是不可变的,仍然可以通过在变量名前添加关键字 mut 来使其可变。代码如下:

fn main() {
    let mut x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}

结果如下:

The value of x is: 5
The value of x is: 6

常量

与不可变变量类似,常量(constants)是绑定到一个名称的不允许被改变的值。

常量与变量的区别:

  • 常量不允许用 mut 修饰
  • 常量不仅默认不可变,且总是不可变
  • 常量声明使用关键字 const 而不是 let,且必须注明值的类型
  • 常量可以在任何作用域中声明,包括全局作用域
  • 常量只能被设置为常量表达式,而不可以是其他任何只能在运行时计算出的值

以下是一个声明常量 THREE_HOURS_IN_SECONDS 的代码示例:

fn main() {
    const THREE_HOURS_IN_SECONDS: u32= 60 * 60 * 3;
    println!("Three hours in seconds is {THREE_HOURS_IN_SECONDS}s");
}

结果如下:

Three hours in seconds is 10800s

变量隐藏

在 Rust 中变量的值不能修改,但是允许定义一个与之前变量同名的新变量。

当定义了一个新的同名变量,在使用变量名时,使用的是新的变量而不是旧的变量,这种情况称为第一个变量被第二个变量隐藏。

第二个变量“遮蔽”了第一个变量,此时任何使用该变量名的行为中都会视为是在使用第二个变量,直到第二个变量自己也被隐藏或第二个变量的作用域(一对 {} 就是变量的作用域)结束。

可以用相同变量名称来隐藏一个变量,以及重复使用 let 关键字来多次隐藏,如下所示:

fn main() {
    let x = 5;
    let x = x + 1;
    {
        let x = x * 2;
        println!("The value of x in the inner scope is: {x}");
    }
    println!("The value of x is: {x}");
}

结果如下:

The value of x in the inner scope is: 12
The value of x is: 6

隐藏与将变量标记为 mut 是有区别的:

特性mut隐藏(shadowing)
允许修改值✅ 是✅ 是(通过新变量)
允许改变类型❌ 否✅ 是
是否是同一个变量✅ 是(在内存中相同)❌ 否(创建了新绑定)
作用域相同作用域每次新 let 是新作用域
是否会影响原变量✅ 是❌ 不会(是新变量)
是否可以变不可变/反之❌ 否(必须在一开始就决定)✅ 是(新绑定可以改变 mut 状态)

数据类型

在 Rust 中,每一个值都属于某一个数据类型(data type),这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。

Rust 的数据类型分为两类数据类型子集:标量和复合。

Rust 是 静态类型(statically typed)语言,也就是说在编译时就必须知道所有变量的类型。根据值及其使用方式,编译器通常可以推断出想要用的类型。当多种类型均有可能时,必须增加类型注解。

标量类型

标量类型代表一个单独的值。Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。

整型

整数是一个没有小数部分的数字。

Rust 中的整型分为两大类:

类别说明
有符号整型可以表示正数和负数(有符号位),i 开头
无符号整型只能表示正数和零,u 开头

整型类型及其大小范围:

类型大小范围(十进制)
i88位-128 到 127
i1616位-32,768 到 32,767
i3232位-2,147,483,648 到 2,147,483,647
i6464位-9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
i128128位非常大的范围(用于加密或大数运算)
isize平台相关32位系统上为 i32,64位系统上为 i64
u88位0 到 255
u1616位0 到 65,535
u3232位0 到 4,294,967,295
u6464位0 到 18,446,744,073,709,551,615
u128128位同上
usize平台相关类似于 isize

整型字面值基本格式:

let a = 42;        // 推断为 i32(默认)
let b = 42u8;      // 明确指定为 u8
let c = 42_i64;    // 下划线分隔的类型后缀形式(等价于 42i64)

为了增强可读性可以使用下划线 _

let big_number = 1_000_000; // = 1000000
let hex = 0xff_ff;          // = 65535

Rust 支持多种进制的数字字面值:

写法说明示例
十进制默认42
十六进制 (0x)十六进制数值0xff
八进制 (0o)八进制数值0o77
二进制 (0b)二进制数值0b1010
单字节字符 (b'')仅限于u8b'A'

整型溢出:

  • Debug 模式:默认检查溢出,溢出会 panic
  • Release 模式:不 panic,采用 wrap-around(回绕)行为
let x: u8 = 255;
let y = x + 1; // Debug 模式下会 panic;Release 下为 0

显式溢出方法:

方法名行为
wrapping_add()溢出时回绕
checked_add()溢出时返回 None
overflowing_add()返回值和是否溢出的布尔值
saturating_add()溢出时返回最大或最小值(饱和行为)
//wrapping_* —— 二进制补码回绕
let x: u8 = 255;
let y = x.wrapping_add(1);
println!("{}", y); // 输出 0,因为 u8 最大值是 255

//checked_* —— 安全检测,有无溢出
let x: u8 = 255;
let result = x.checked_add(1);
match result {
    Some(val) => println!("Result: {}", val),
    None => println!("Overflow occurred!"),
}

//overflowing_* —— 返回值和溢出标志
let x: u8 = 255;
let (val, overflowed) = x.overflowing_add(1);
println!("val = {}, overflowed = {}", val, overflowed); // val = 0, overflowed = true

//saturating_* —— 饱和计算(到边界)
let x: u8 = 250;
let y = x.saturating_add(10);
println!("{}", y); // 输出 255,不会变成 4,而是饱和到最大值
浮点型

Rust 也有两个原生的浮点数类型,它们是带小数点的数字。

Rust 的浮点数类型是 f32 和 f64,分别占 32 位和 64 位。默认类型是 f64。

浮点数采用 IEEE-754 标准表示。f32 是单精度浮点数,f64 是双精度浮点数。

fn main() {
    let x = 2.0; // f64

    let y: f32 = 3.0; // f32
}
数值运算

Rust 中的所有数字类型都支持基本数学运算:加法、减法、乘法、除法和取余。整数除法会向零舍入到最接近的整数。

代码示例:

fn main() {
    // addition
    let sum = 5 + 10;
    println!("The sum of 5 and 10 is {}", sum);

    // subtraction
    let difference = 95.5 - 4.3;
    println!("The difference of 95.5 and 4.3 is {}", difference);

    // multiplication
    let product = 4 * 30;
    println!("The product of 4 and 30 is {}", product);

    // division
    let quotient = 56.7 / 32.2;
    println!("The quotient of 56.7 and 32.2 is {}", quotient);
    let truncated = -5 / 3; // 结果为 -1
    println!("The truncated result of -5 / 3 is {}", truncated);

    // remainder
    let remainder = 43 % 5;
    println!("The remainder of 43 % 5 is {}", remainder);
}

结果如下:

The sum of 5 and 10 is 15
The difference of 95.5 and 4.3 is 91.2
The product of 4 and 30 is 120
The quotient of 56.7 and 32.2 is 1.7608695652173911
The truncated result of -5 / 3 is -1
The remainder of 43 % 5 is 3
布尔型

Rust 中的布尔类型有两个可能的值:truefalse。Rust 中的布尔类型使用 bool 表示。例如:

fn main() {
    let t = true;

    let f: bool = false; //带有显式类型注释
}

使用布尔值的主要场景是条件表达式,例如 if 表达式。

字符类型

Rust 的 char 类型是语言中最原生的字母类型。下面是一些声明 char 值的例子:

fn main() {
    let c = 'z';
    let z: char = 'ℤ'; // with explicit type annotation
    let heart_eyed_cat = '😻';
}

用单引号声明 char 字面量,而与之相反的是,使用双引号声明字符串字面量。Rust 的 char 类型的大小为四个字节,并代表了一个 Unicode 标量值,这意味着它可以比 ASCII 表示更多内容。

复合类型

复合类型可以将多个值组合成一个类型。Rust 有两个原生的复合类型:元组(tuple)和数组(array)。

元组

元组是一个将多个其他类型的值组合进一个复合类型的主要方式。元组长度固定:一旦声明,其长度不会增大或缩小。

使用包含在圆括号中的逗号分隔的值列表来创建一个元组。元组中的每一个位置都有一个类型,而且这些不同值的类型也不必是相同的。这个例子中使用了可选的类型注解:

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
}

输出元组不能用 {},要用 Debug trait 中的 {:?}{:#?}

println!("The value of tup is: {:?}", tup);

为了从元组中获取单个值,可以使用模式匹配来解构元组值,像这样:

fn main() {
    let tup = (500, 6.4, 1);

    let (x, y, z) = tup;
    
    println!("The value of x is: {x}");
    println!("The value of y is: {y}");
    println!("The value of z is: {z}");
}

使用了 let 和一个模式将 tup 分成了三个不同的变量,xyz。这叫做 解构,因为它将一个元组拆成了三个部分。

也可以使用点号(.)后跟值的索引来直接访问它们。例如:

fn main() {
    let x: (i32, f64, u8) = (500, 6.4, 1);

    let five_hundred = x.0;
    println!("The value of five_hundred is: {}", five_hundred);

    let six_point_four = x.1;
    println!("The value of six_point_four is: {}", six_point_four);

    let one = x.2;
    println!("The value of one is: {}", one);
}

结果如下:

The value of five_hundred is: 500
The value of six_point_four is: 6.4
The value of one is: 1

不带任何值的元组有个特殊的名称,叫做 单元(unit) 元组。这种值以及对应的类型都写作 (),表示空值或空的返回类型。如果表达式不返回任何其他值,则会隐式返回单元值。

数组

另一个包含多个值的方式是 数组array)。与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,Rust 中的数组长度是固定的。

将数组的值写成在方括号内,用逗号分隔:

fn main() {
    let a = [1, 2, 3, 4, 5];
}

可以像这样编写数组的类型:在方括号中包含每个元素的类型,后跟分号,再后跟数组元素的数量:

let a: [i32; 5] = [1, 2, 3, 4, 5];

还可以通过在方括号中指定初始值加分号再加元素个数的方式来创建一个每个元素都为相同值的数组:

let a = [3; 5]; //let a = [3, 3, 3, 3, 3];

访问数组元素跟访问元组中的单个值类似,也是通过索引来访问:

fn main() {
    let a = [1, 2, 3, 4, 5];

    let first = a[0];
    println!("The first element is: {}", first);
    let second = a[1];
    println!("The second element is: {}", second);
}

结果如下:

The first element is: 1
The second element is: 2

跟其他编程语言一样,Rust 中也要预防索引越位。

函数

函数在 Rust 代码中非常普遍。已经见过语言中最重要的函数之一:main 函数,它是很多程序的入口点。你也见过 fn 关键字,它用来声明新函数。

Rust 代码中的函数和变量名使用 snake case 规范风格。在 snake case 中,所有字母都是小写并使用下划线分隔单词。这是一个包含函数定义示例的程序:

fn main() {
    println!("Hello, world!");

    another_function();
}

fn another_function() {
    println!("Another function.");
}

参数

跟其他编程语言一样,Rust 可以定义为拥有参数的函数,参数是特殊变量,是函数签名的一部分。当函数拥有参数(形参)时,可以为这些参数提供具体的值(实参)。

fn main() {
    another_function(5); //传入的5是实参
}

fn another_function(x: i32) { //x是形参
    println!("The value of x is: {x}");
}

Rust 规定:要求在函数定义中提供类型注解。也就是说在函数签名中,必须声明每个参数的类型。

fn main() {
    print_labeled_measurement(5, 'h');
}

fn print_labeled_measurement(value: i32, unit_label: char) {
    println!("The measurement is: {value}{unit_label}");
}

语句与表达式

Rust 是一门基于表达式的语言,函数体由一系列的语句和一个可选的结尾表达式构成:

  • 语句(Statements)是执行一些操作但不返回值的指令
  • 表达式(Expressions)计算并产生一个值

常见语句:

  1. 变量绑定语句

    let x = 5;
    
  2. 函数调用语句

    println!("Hello, world!");
    
  3. 表达式后加分号也变成语句

    3 + 4; // 这是表达式加上分号,变成了语句,不返回值
    

常见表达式:

  1. 算术表达式

    let y = 3 + 4; // 表达式 3 + 4 的结果是 7
    
  2. 函数返回值

    fn add(a: i32, b: i32) -> i32 {
        a + b  // 没有分号,是表达式,返回值
    }
    
  3. if 表达式

    let max = if x > y { x } else { y }; // if 是一个表达式
    
  4. 代码块表达式

    let z = {
        let a = 5;
        let b = 10;
        a + b // 最后一行没有分号,整个 block 是表达式
    }; // z = 15
    

函数返回值

函数可以向调用它的代码返回值。虽然并不对返回值命名,但要在箭头(->)后声明它的类型。在 Rust 中,函数的返回值等同于函数体最后一个表达式的值。使用 return 关键字和指定值,可从函数中提前返回;但大部分函数隐式的返回最后的表达式。这是一个有返回值的函数的例子:

fn five() -> i32 {
    5
}

fn main() {
    let x = five();

    println!("The value of x is: {x}");
}

输出结果如下:

The value of x is: 5

let x = five(); 这一行表明我们使用函数的返回值初始化一个变量。因为 five 函数返回 5。five 函数没有参数并定义了返回值类型,不过函数体只有单单一个 5 也没有分号,因为这是一个表达式,需要返回它的值。

看另一个例子:

fn main() {
    let x = plus_one(5);

    println!("The value of x is: {x}");
}

fn plus_one(x: i32) -> i32 {
    x + 1;
}

运行代码产生错误:

error[E0308]: mismatched types
 --> src/main.rs:7:24
  |
7 | fn plus_one(x: i32) -> i32 {
  |    --------            ^^^ expected `i32`, found `()`
  |    |
  |    implicitly returns `()` as its body has no tail or `return` expression
8 |     x + 1;
  |          - help: remove this semicolon to return this value

主要的错误信息,“mismatched types”(类型不匹配),揭示了代码的核心问题。函数 plus_one 的定义说明它要返回一个 i32 类型的值,不过语句并不会返回值,使用单位类型 ()表示不返回值。因为不返回值与函数定义相矛盾,从而出现一个错误。

函数返回值不要以分号结尾!!!

控制流

根据条件是否为真来决定是否执行某些代码,以及根据条件是否为真来重复运行一段代码的能力是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是 if 表达式和循环。

使用 if 表达式控制条件

if 表达式

if 表达式允许根据条件执行不同的代码分支。提供一个条件并表示如果条件满足,运行这段代码;如果条件不满足,不运行这段代码。

fn main() {
    let number = 3;

    if number < 5 {
        println!("condition was true");
    } else {
        println!("condition was false");
    }
}

所有的 if 表达式都以 if 关键字开头,其后跟一个条件。在这个例子中,条件检查变量 number 的值是否小于 5。在条件为 true 时希望执行的代码块位于紧跟条件之后的大括号中。

也可以包含一个可选的 else 表达式来提供一个在条件为 false 时应当执行的代码块。如果不提供 else 表达式并且条件为 false 时,程序会直接忽略 if 代码块并继续执行下面的代码。

输出结果如下:

condition was true

得注意的是代码中的条件 必须bool 值。

使用 else if 处理多重条件

可以将 else if 表达式与 ifelse 组合来实现多重条件。例如:

fn main() {
    let number = 6;

    if number % 4 == 0 {
        println!("number is divisible by 4");
    } else if number % 3 == 0 {
        println!("number is divisible by 3");
    } else if number % 2 == 0 {
        println!("number is divisible by 2");
    } else {
        println!("number is not divisible by 4, 3, or 2");
    }
}

当执行这个程序时,它按顺序检查每个 if 表达式并执行第一个条件为 true 的代码块。注意即使 6 可以被 2 整除,也不会输出 number is divisible by 2,更不会输出 else 块中的 number is not divisible by 4, 3, or 2。原因是 Rust 只会执行第一个条件为 true的代码块,并且一旦它找到一个以后,甚至都不会检查剩下的条件了。

在 let 语句中使用 if

因为 if 是一个表达式,我们可以在 let 语句的右侧使用它,例如:

fn main() {
    let condition = true;
    let number = if condition { 5 } else { 6 };

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

number 变量将会绑定到表示 if 表达式结果的值上:

The value of number is: 5

代码块的值是其最后一个表达式的值,而数字本身就是一个表达式。if 的每个分支的可能的返回值都必须是相同类型.

使用循环重复执行

多次执行同一段代码是很常用的,Rust 为此提供了多种循环。一个循环执行循环体中的代码直到结尾并紧接着回到开头继续执行。

使用 loop 重复执行代码

loop 关键字告诉 Rust 一遍又一遍地执行一段代码直到明确要求停止。

fn main() {
    loop {
        println!("again!");
    }
}

当运行这个程序时,我们会看到连续的反复打印 again!,直到手动停止程序。大部分终端都支持一个快捷键,ctrl+c,来终止一个陷入无限循环的程序。

除了用 ctrl+c 手动停止外,Rust 提供了一种从代码中跳出循环的方法。可以使用 break关键字来告诉程序何时停止循环。循环中的 continue 关键字告诉程序跳过这个循环迭代中的任何剩余代码,并转到下一个迭代。

从循环返回值

在循环的过程中,可能会需要将操作的结果返回给其它的代码。如果将返回值加入你用来停止循环的 break 表达式,它会被停止的循环返回:

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    println!("The result is {result}");
}

输出结果如下:

The result is 20

跟传统的 C/C++、Java 不同,Rust 中的 break 还能返回值。

循环标签

如果存在嵌套循环,breakcontinue 应用于此时最内层的循环。可以选择在一个循环上指定一个循环标签,然后将标签与 breakcontinue 一起使用,使这些关键字应用于已标记的循环而不是最内层的循环。下面是一个包含两个嵌套循环的示例:

fn main() {
    let mut count = 0;
    'counting_up: loop {
        println!("count = {count}");
        let mut remaining = 10;

        loop {
            println!("remaining = {remaining}");
            if remaining == 9 {
                break;
            }
            if count == 2 {
                break 'counting_up;
            }
            remaining -= 1;
        }

        count += 1;
    }
    println!("End count = {count}");
}

外层循环有一个标签 counting_up,它将从 0 数到 2。没有标签的内部循环从 10 向下数到 9。第一个没有指定标签的 break 将只退出内层循环。break 'counting_up; 语句将退出外层循环。这个代码打印:

count = 0
remaining = 10
remaining = 9
count = 1
remaining = 10
remaining = 9
count = 2
remaining = 10
End count = 2
while 条件循环

在程序中计算循环的条件也很常见。当条件为 true,执行循环。当条件不再为 true,调用 break 停止循环。Rust 为此内置了一个语言结构,它被称为 while 循环。

fn main() {
    let mut number = 3;
		
  	//当条件为 true 就执行,否则退出循环
    while number != 0 {
        println!("{number}!");

        number -= 1;
    }

    println!("TIME OVER!!!");
}

输出结果如下:

3!
2!
1!
TIME OVER!!!

可以使用 while 结构来遍历集合中的元素,比如数组。例如:

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

    while index < 5 {
        println!("the value is: {}", a[index]);

        index += 1;
    }
}

这里,代码对数组中的元素进行计数。它从索引 0 开始,并接着循环直到遇到数组的最后一个索引(这时,index < 5 不再为真)。输出结果如下:

the value is: 10
the value is: 20
the value is: 30
the value is: 40
the value is: 50

但是这种手动设置 index < 5 往往会因为粗心或者不注意而导致索引越位的现象,进而导致程序出错,因此将使用下面的方法来遍历数组。

使用 for 遍历集合

作为更简洁的替代方案,可以使用 for 循环来对一个集合的每个元素执行一些代码:

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

    for element in a {
        println!("the value is: {element}");
    }
}

当然,Rust 中的 for 循环也是有带索引的形式:

fn main() {
    for number in (1..4).rev() {
        println!("{number}!");
    }
    println!("TIME OVER!!!");
}

in (1...4) 表示的是 1 到 4 但不包括 4 的数字,即 1、2、3。输出结果如下:

3!
2!
1!
TIME OVER!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2341233.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AI-Sphere-Butler之如何使用Llama factory LoRA微调Qwen2-1.5B/3B专属管家大模型

环境&#xff1a; AI-Sphere-Butler WSL2 英伟达4070ti 12G Win10 Ubuntu22.04 Qwen2.-1.5B/3B Llama factory llama.cpp 问题描述&#xff1a; AI-Sphere-Butler之如何使用Llama factory LoRA微调Qwen2-1.5B/3B管家大模型 解决方案&#xff1a; 一、准备数据集我这…

协同推荐算法实现的智能商品推荐系统 - [基于springboot +vue]

&#x1f6cd;️ 智能商品推荐系统 - 基于springboot vue &#x1f680; 项目亮点 欢迎来到未来的购物体验&#xff01;我们的智能商品推荐系统就像您的私人购物顾问&#xff0c;它能读懂您的心思&#xff0c;了解您的喜好&#xff0c;为您精心挑选最适合的商品。想象一下&am…

Jenkins的地位和作用

所处位置 Jenkins 是一款开源的自动化服务器&#xff0c;广泛应用于软件开发和测试流程中&#xff0c;主要用于实现持续集成&#xff08;CI&#xff09;和持续部署&#xff08;CD&#xff09;。它在开发和测试中的位置和作用可以从以下几个方面来理解&#xff1a; 1. 在开发和测…

【集合】底层原理实现及各集合之间的区别

文章目录 集合2.1 介绍一下集合2.2 集合遍历的方法2.3 线程安全的集合2.4 数组和集合的区别2.5 ArrayList和LinkedList的区别2.6 ArrayList底层原理2.7 LinkedList底层原理2.8 CopyOnWriteArrayList底层原理2.9 HashSet底层原理2.10 HashMap底层原理2.11 HashTable底层原理2.12…

srp batch

参考网址&#xff1a; Unity MaterialPropertyBlock 正确用法&#xff08;解决无法合批等问题&#xff09;_unity_define_instanced_prop的变量无法srp合批-CSDN博客 URP | 基础CG和HLSL区别 - 哔哩哔哩 (bilibili.com) 【直播回放】Unity 批处理/GPU Instancing/SRP Batche…

【Linux运维涉及的基础命令与排查方法大全】

文章目录 前言1、计算机网络常用端口2、Kali Linux中常用的命令3、Kali Linux工具的介绍4、Ubuntu没有网络连接解决方法5、获取路由6、数据库端口 前言 以下介绍计算机常见的端口已经对应的网络协议&#xff0c;Linux中常用命令&#xff0c;以及平时运维中使用的排查网络故障的…

Webview+Python:用HTML打造跨平台桌面应用的创新方案

目录 一、技术原理与优势分析 1.1 架构原理 1.2 核心优势 二、开发环境搭建 2.1 安装依赖 2.2 验证安装 三、核心功能开发 3.1 基础窗口管理 3.2 HTML↔Python通信 JavaScript调用Python Python调用JavaScript 四、高级功能实现 4.1 系统级集成 4.2 多窗口管理 五…

克服储能领域的数据处理瓶颈及AI拓展

对于储能研究人员来说&#xff0c;日常工作中经常围绕着一项核心但有时令人沮丧的任务&#xff1a;处理实验数据。从电池循环仪的嗡嗡声到包含电压和电流读数的大量电子表格&#xff0c;研究人员的大量时间都花在了提取有意义的见解上。长期以来&#xff0c;该领域一直受到对专…

包含物体obj与相机camera的 代数几何代码解释

反余弦函数的值域在 [0, pi] 斜体样式 cam_pose self._cameras[hand_realsense].camera.get_model_matrix() # cam2world# 物体到相机的向量 obj_tcp_vec cam_pose[:3, 3] - self.obj_pose.p dist np.linalg.norm(obj_tcp_vec) # 物体位姿的旋转矩阵 obj_rot_mat self.ob…

mybatis实现增删改查1

文章目录 19.MyBatis查询单行数据MapperScan 结果映射配置核心文件Results自定义映射到实体的关系 多行数据查询-完整过程插入数据配置mybatis 控制台日志 更新数据删除数据小结通过id复用结果映射模板xml处理结果映射 19.MyBatis 数据库访问 MyBatis&#xff0c;MyBatis-Plus…

Git,本地上传项目到github

一、Git的安装和下载 https://git-scm.com/ 进入官网&#xff0c;选择合适的版本下载 二、Github仓库创建 点击右上角New新建一个即可 三、本地项目上传 1、进入 要上传的项目目录&#xff0c;右键&#xff0c;选择Git Bash Here&#xff0c;进入终端Git 2、初始化临时仓库…

基于flask+vue框架的灯饰安装维修系统u49cf(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,工单人员,服务项目,订单记录,服务记录,评价记录 开题报告内容 基于 FlaskVue 框架的灯饰安装维修系统开题报告 一、选题背景与意义 &#xff08;一&#xff09;选题背景 随着城市化进程的加速与居民生活品质的显著提升&#xf…

【算法】BFS-解决FloodFill问题

目录 FloodFill问题 图像渲染 岛屿数量 岛屿的最大面积 被围绕的区域 FloodFill问题 FloodFill就是洪水灌溉的意思&#xff0c;假设有下面的一块田地&#xff0c;负数代表是凹地&#xff0c;正数代表是凸地&#xff0c;数字的大小表示凹或者凸的程度。现在下一场大雨&…

GIS开发笔记(10)基于osgearth实现二三维地图的一键指北功能

一、实现效果 二、实现原理 获取视图及地图操作器,通过地图操作器来重新设置视点,以俯仰角 (0.0)和偏航角 (-90.0)来设置。 osgEarth::Util::Viewpoint(…) 这里创建了一个新的 Viewpoint 对象,表示一个特定的视角。构造函数的参数是: 第一个参数:是视角名称。 后面的 6 个…

window上 elasticsearch v9.0 与 jmeter5.6.3版本 冲突,造成es 启动失败

[2025-04-22T11:00:22,508][ERROR][o.e.b.Elasticsearch ] [AIRUY] fatal exception while booting Elasticsearchjava.nio.file.NoSuchFileException: D:\Program Files\apache-jmeter-5.6.3\lib\logkit-2.0.jar 解决方案&#xff1a; 降低 es安装版本 &#xff0c;选择…

【C++初阶】第15课—模版进阶

文章目录 1. 模版参数2. 模版的特化2.1 概念2.2 函数模版特化2.3 类模板特化2.3.1 全特化2.3.2 偏特化 3. 模版的分离和编译4. 总结 1. 模版参数 模版参数分为类型形参和非类型参数之前我们写过的大量代码&#xff0c;都是用模版定义类的参数类型&#xff0c;跟在class和typena…

黑阈免激活版:智能管理后台,优化手机性能

在使用安卓手机的过程中&#xff0c;许多用户会遇到手机卡顿、电池续航不足等问题。这些问题通常是由于后台运行的应用程序过多&#xff0c;占用大量系统资源导致的。今天&#xff0c;我们要介绍的 黑阈免激活版&#xff0c;就是这样一款由南京简域网络科技工作室开发的手机辅助…

Mujoco robosuite 机器人模型

import ctypes import os# 获取当前脚本所在的目录 script_dir os.path.dirname(os.path.abspath(__file__))# 构建库文件的相对路径 lib_relative_path os.path.join(dynamic_models, UR5e, Jb.so)# 拼接成完整的路径 lib_path os.path.join(script_dir, lib_relative_path…

K8s:概念、特点、核心组件与简单应用

一、引言 在当今云计算和容器技术蓬勃发展的时代&#xff0c;Kubernetes&#xff08;简称 K8s&#xff09;已成为容器编排领域的事实标准。它为管理容器化应用提供了高效、可靠的解决方案&#xff0c;极大地简化了应用的部署、扩展和运维过程。无论是小型初创公司还是大型企业…

STM32的定时器输出PWM时,死区时间(DTR)如何计算

在 STM32F429&#xff08;以及所有 STM32F4 “高级定时器”&#xff09;中&#xff0c;死区时间由 TIMx_BDTR 寄存器的 8 位 “Dead‑Time Generator” 字段 DTG[7:0] 来配置。其计算分三步&#xff1a; 计算死区时钟周期 tDTS TIM1 时钟源为 APB2 定时器时钟&#xff08;PCL…