详解Rust中三种循环(loop,while,for)的使用

目录
  • 楔子
  • loop 循环
  • while 循环
  • for 循环

楔子

我们常常需要重复执行同一段代码,针对这种场景,Rust 提供了多种循环(loop)工具。一个循环会执行循环体中的代码直到结尾,并紧接着回到开头继续执行。

而 Rust 提供了 3 种循环:loop、while 和 for,下面逐一讲解。

loop 循环

我们可以使用 loop 关键字来指示 Rust 反复执行某一段代码,直到我们显式地声明退出为止。

fn main() {
    loop {
        println!("hello world");
    }
}

这段代码会不停地在终端中打印 hello world,我们只能使用 Ctrl + C 来终止这种陷入无限循环的程序。当然,Rust 提供了另外一种更加可靠的循环退出方式,可以在循环中使用 break 关键字来通知程序退出循环。

fn main() {
    let mut x = 1;  // x 可变
    loop {
        println!("hello world");
        if x == 5 {
            break;
        }
        // 注意 x 必须是可变的
        // 否则此处报错
        x += 1;
    }
    /*
    hello world
    hello world
    hello world
    hello world
    hello world
     */
}

打印了五遍就停止了,没什么好说的。但 loop 循环还支持返回值,我们举个例子:

fn main() {
    let mut x = 1;
    let y = loop {
        if x == 5 {
            // break 之后的值就是整个 loop 的返回值
            break x * 2;
        }
        x += 1;
    };
    println!("y = {}", y);  // y = 10
}

如果 break 后面没有值,那么整个 loop 返回的就是空元组:

fn main() {
    let mut x = 1;
    let y = loop {
        if x == 5 {
            break;
        }
        x += 1;
    };
    println!("y = {:?}", y);  // y = ()
}

需要说明的是,无论 break 后面有没有分号,它都是整个 loop 循环的返回值。

既然是 loop 循环是一个表达式,那么除了赋值给一个变量之外,肯定也可以作为函数的返回值:

fn f() -> i32 {
    let mut x = 1;
    loop {
        if x == 5 {
            break x * 2;
        }
        x += 1;
    } // 此处结尾不可以有分号
}

fn main() {
    println!("{}", f());  // 10
}

注意 loop 循环的最后一定不能加分号,因为加了就会变成语句,而语句不会返回任何内容。所以在 if 表达式的时候我们啰嗦了那么多关于表达式、分号的内容,就是因为这些概念在循环中同样会体现。

下面的做法是错误的:

fn f() -> i32 {
    let mut x = 1;
    loop {
        if x == 5 {
            break x * 2;
        }
        x += 1;
    };  // 这里加上了分号
}

我们一定不能这么做,因为这会让 loop 循环变成语句,而下面又没有内容了,因此函数 f 会默认返回空元组。而函数的返回值签名是 i32,于是出现矛盾,造成编译错误。那么下面这个例子可以吗?

fn f() -> i32 {
    let mut x = 1;
    loop {
        if x == 5 {
            // break 语句结尾有没有分号
            // 并不重要
            break x * 2;
        }
        x += 1;
    }
    33
}

答案是依旧不行,因为 loop 循环是一个表达式,而它下面还有表达式,违反了我们之前说的函数末尾只能有一个表达式的原则。但是有一个例外,相信你已经猜到了,就是当 loop 表达式返回元组的时候,那么会忽略掉。

fn f() -> i32 {
    let mut x = 1;
    loop {
        if x == 5 {
            // 等价于 break;
            break ();  
        }
        x += 1;
    }
    33
}

此时是没有问题的,以上就是 loop 循环。

while 循环

另外一种常见的循环模式是在每次执行循环体之前都判断一次条件,如果条件为真,则执行代码片段,如果条件为假、或在执行过程中碰到 break 就退出当前循环。

这种模式可以通过 loop、if、else 及 break 关键字的组合使用来实现,有兴趣的话可以试着完成这一功能。不过由于这种模式太过于常见,所以 Rust 为此提供了一个内置的语言结构:while 条件循环。

fn main() {
    let mut x = 1;
    while x <= 5 {
        println!("hello world");
        x += 1;
    }
}

执行完之后会打印 5 次 hello world,然后是返回值的问题,while 循环不可以像 loop 一样 break 一个值,也就是说它只能默认返回空元组。

fn f() -> i32 {
    let mut x = 1;
    while x <= 5 {
        if x == 3 {
            break;
        }
        x += 1
    }
    // 没有下面这个 33,那么该函数就是非法的
    33
}

fn main() {
    println!("{:?}", f());  // 33

    // 当然 while 循环也可以赋值给一个变量
    // 因为只可能返回空元组,所以这么做没有什么意义
    let x = while 1 <= 2 {
        break;
    };
    println!("{:?}", x);  // ()
}

而当 break 后面有值的时候,会编译错误,假设我们 break 123。

告诉我们带有值的 break 只能出现在 loop 循环中,而 while 循环是不支持的。另外即便 break 一个空元组也是不允许的,尽管 while 循环会默认返回空元组。

for 循环

我们遍历一个数组可以选择 loop 循环、while 循环,但是这样容易因为使用了不正确的索引长度而使程序崩溃。

fn traverse1() {
    let arr = [1, 2, 3, 4, 5];
    let mut sum: i32 = 0;
    let mut index: usize = 0;
    loop {
        if index < 5 {
            // 通过索引获取元素
            // 索引必须是 usize 类型
            sum += arr[index];
        } else {
            break;
        }
        index += 1;
    }
    println!("sum([1, 2, 3, 4, 5]) = {}", sum);
}

fn traverse2() {
    let arr = [1, 2, 3, 4, 5];
    let mut sum: i32 = 0;
    let mut index: usize = 0;
    while index < 5 {
        sum += arr[index];
        index += 1;
    }
    println!("sum([1, 2, 3, 4, 5]) = {}", sum);
}

fn main() {
    traverse1();  
    // sum([1, 2, 3, 4, 5]) = 15
    traverse2();  
    // sum([1, 2, 3, 4, 5]) = 15
}

虽然成功遍历了,但如果索引越界的话就会发生错误,因此可以使用 for 循环这种更简明的方法来遍历集合中的每一个元素。

fn traverse() {
    let arr = [1, 2, 3, 4, 5];
    let mut sum: i32 = 0;
    for element in arr {
        sum += element;
    }
    println!("sum([1, 2, 3, 4, 5]) = {}", sum);
}

fn main() {
    traverse();  
    // sum([1, 2, 3, 4, 5]) = 15
}

结果是一样的,但我们增强了代码的安全性,不会出现诸如越界访问或漏掉某些元素之类的问题。

假如后期修改代码,我们从 arr 数组中移除了某个元素,却忘记将循环中的条件更新为 while index < 4,那么再次运行代码就会发生崩溃。而使用 for 循环的话,就不需要时常惦记着在更新数组元素数量时,还要去修改代码的其他部分。

for 循环的安全性和简捷性使它成为了 Rust 中最为常用的循环结构,即便是为了实现循环特定次数的任务,大部分的 Rust 开发者也会选择使用 for 循环。我们可以配合标准库中提供的 Range 来实现这一目的,它被用来生成从一个数字开始到另一个数字结束之前的所有数字序列。

fn main() {
    for number in 1..4 {
        println!("number = {}", number);
    }
    /*
    number = 1
    number = 2
    number = 3
     */

    // 还可以逆序输出
    for number in (1..4).rev() {
        println!("number = {}", number);
    }
    /*
    number = 3
    number = 2
    number = 1
     */
}

代码是不是更加精炼了呢。

到此这篇关于详解Rust中三种循环(loop,while,for)的使用的文章就介绍到这了,更多相关Rust循环内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Rust 实现 async/await的详细代码

    目录 Future Wake & Context 为什么需要 executor ? 什么是 waker ? async/await Executor Waker struct 到 ArcWake trait FuturesUnordered 单线程 executor 线程池 executor 总结 异步编程在 Rust 中的地位非常高,很多 crate 尤其是多IO操作的都使用了 async/await. 首先弄清楚异步编程的几个基本概念: Future Future 代表一个可在未来某个时候获

  • Rust中的Struct使用示例详解

    Structs是RUST中比较常见的自定义类型之一,又可以分为StructStruct,TupleStruct,UnitStruct三个类型,结合泛型.Trait限定.属性.可见性可以衍生出很丰富的类型. 结构体 1.定义 pub struct User { user_id : u32, user_name: String, is_vip : bool, } 2.实例化这里初始化必须全部给所有的成员赋值,不像C++,可以单独初始化某个值 let user : User = User { user

  • Rust 入门之函数和注释实例详解

    目录 写在前面 函数 参数 语句和表达式 返回值 注释 写在前面 今天我们来学习 Rust 中的函数,最后会捎带介绍一下如何在 Rust 中写注释.也是比较轻量级的一节,大家快速过一下即可. 函数 函数本身是各个语言都支持的类型,我们此前已经多次使用 fn main() 这个函数来承载业务逻辑,fn 可以用来声明一个函数,而 main 函数跟其他语言一样,可以理解为程序启动的[起点],一切逻辑从这里开始. Rust 本身的命名规范是[snake case],即下划线 + 小写,这个其实各个语言都

  • 详解Rust中三种循环(loop,while,for)的使用

    目录 楔子 loop 循环 while 循环 for 循环 楔子 我们常常需要重复执行同一段代码,针对这种场景,Rust 提供了多种循环(loop)工具.一个循环会执行循环体中的代码直到结尾,并紧接着回到开头继续执行. 而 Rust 提供了 3 种循环:loop.while 和 for,下面逐一讲解. loop 循环 我们可以使用 loop 关键字来指示 Rust 反复执行某一段代码,直到我们显式地声明退出为止. fn main() {     loop {         println!("

  • 详解Thymeleaf的三种循环遍历方式

    目录 循环遍历list集合 1.实体类 2.控制类 3.each.html 循环遍历map集合 1.控制类 2.each.html 循环遍历数组 循环遍历list集合 1.实体类 使用lombok插件,省去getter和setter,toString等方法的书写 代码 package com.springboot_thyleaf2.model; import lombok.Data; @Data public class User { private Integer id; private St

  • 详解Java中两种分页遍历的使用姿势

    在日常开发中,分页遍历迭代的场景可以说非常普遍了,比如扫表,每次捞100条数据,然后遍历这100条数据,依次执行某个业务逻辑:这100条执行完毕之后,再加载下一百条数据,直到扫描完毕 那么要实现上面这种分页迭代遍历的场景,我们可以怎么做呢 本文将介绍两种使用姿势 常规的使用方法 借助Iterator的使用姿势 1. 数据查询模拟 首先mock一个分页获取数据的逻辑,直接随机生成数据,并且控制最多返回三页 public static int cnt = 0; private static List

  • 一文详解JS中的事件循环机制

    目录 前言 1.JavaScript是单线程的 2.同步和异步 3.事件循环 前言 我们知道JavaScript 是单线程的编程语言,只能同一时间内做一件事,按顺序来处理事件,但是在遇到异步事件的时候,js线程并没有阻塞,还会继续执行,这又是为什么呢?本文来总结一下js 的事件循环机制. 1.JavaScript是单线程的 JavaScript 是一种单线程的编程语言,只有一个调用栈,决定了它在同一时间只能做一件事.在代码执行的时候,通过将不同函数的执行上下文压入执行栈中来保证代码的有序执行.在

  • 详解Python中4种超参自动优化算法的实现

    目录 一.网格搜索(Grid Search) 二.随机搜索(Randomized Search) 三.贝叶斯优化(Bayesian Optimization) 四.Hyperband 总结 大家好,要想模型效果好,每个算法工程师都应该了解的流行超参数调优技术. 今天我给大家总结超参自动优化方法:网格搜索.随机搜索.贝叶斯优化 和 Hyperband,并附有相关的样例代码供大家学习. 一.网格搜索(Grid Search) 网格搜索是暴力搜索,在给定超参搜索空间内,尝试所有超参组合,最后搜索出最优

  • 详解javaweb中jstl如何循环List中的Map数据

    详解javaweb中jstl如何循环List中的Map数据 第一种方式: 1:后台代码(测试) List<Map<String, Object>> list = new ArrayList<Map<String,Object>>(); Map<String, Object> map = null; for (int i = 0; i < 4; i++) { map = new HashMap<String, Object>();

  • 详解Python的三种拷贝方式

    在练习列表的操作的时候我发现赋值之后的列表会随着被赋值的列表改变而改变,就像是C语言中用指向同一实际变量的指针进行操作一样.这是因为Python中有三种拷贝方式:浅拷贝.深拷贝和赋值拷贝. 赋值拷贝就像是定义新指针并指向了同一内存区域,对任意一个列表名进行操作,其他的也会变化. 深拷贝的作用是完全拷贝一个列表A并赋值给另一列表B.以下是深度拷贝与列表操作的样例.记得在使用深拷贝的时候要引入copy包. import copy #对列表的增删改 numbers_Ori = ['one', 'two

  • 详解SpringBoot的三种缓存技术(Spring Cache、Layering Cache 框架、Alibaba JetCache 框架)

    引言 ​前两天在写一个实时数据处理的项目,项目要求是 1s 要处理掉 1k 的数据,这时候显然光靠查数据库是不行的,技术选型的时候老大跟我提了一下使用 Layering-Cache 这个开源项目来做缓存框架. ​之间问了一下身边的小伙伴,似乎对这块了解不多.一般也就用用 Redis 来缓存,应该是很少用多级缓存框架来专门性的管理缓存吧. ​趁着这个机会,我多了解了一些关于 SpringBoot 中缓存的相关技术,于是有了这篇文章! 在项目性能需求比较高时,就不能单单依赖数据库访问来获取数据了,必

  • 详解CocosCreator中几种计时器的使用方法

    一.setTimeOut 3秒后打印abc.只执行一次. setTimeout(()=>{console.log("abc"); }, 3000); 删除计时器,3秒后不会输出abc. let timeIndex; timeIndex = setTimeout(()=>{console.log("abc"); }, 3000); clearTimeout(timeIndex); setTimeout这样写,test函数中输出的this是Window对象

  • 详解JS中你不知道的各种循环测速

    前言 在测试循环速度之前,我们先来创建一个有 100 万数据的数组: const len = 100 * 10000; const arr = []; for (let i = 0; i < len; i++) { arr.push(Math.floor(Math.random() * len)); } 测试环境为: 1.电脑:iMac(10.13.6): 2.处理器:4.2 GHz Intel Core i7: 3.浏览器:Chrome(89.0.4389.82) 1. for 循环 for

随机推荐