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

 更新时间:2022年09月29日 14:53:44   作者:古明地觉  
我们常常需要重复执行同一段代码,针对这种场景,Rust 提供了多种循环(loop)工具。一个循环会执行循环体中的代码直到结尾,并紧接着回到开头继续执行。而 Rust 提供了 3 种循环: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循环内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 深入了解Rust中函数与闭包的使用

    深入了解Rust中函数与闭包的使用

    本文主要介绍一下Rust函数相关的内容,首先函数我们其实一直都在用,所以函数本身没什么可说的,我们的重点是与函数相关的闭包、高阶函数、发散函数,感兴趣的可以学习一下
    2022-11-11
  • Rust如何使用config配置API

    Rust如何使用config配置API

    这篇文章主要介绍了Rust如何使用config配置API,这里记录了如何声明配置类型,读取配置,通过环境变量来覆盖配置值等开发中常见的动作,需要的朋友可以参考下
    2023-11-11
  • Rust在写库时实现缓存的操作方法

    Rust在写库时实现缓存的操作方法

    Moka是一个用于Rust的高性能缓存库,它提供了多种类型的缓存数据结构,包括哈希表、LRU(最近最少使用)缓存和 支持TTL(生存时间)缓存,这篇文章给大家介绍Rust在写库时实现缓存的相关知识,感兴趣的朋友一起看看吧
    2024-01-01
  • vscode搭建rust开发环境的图文教程

    vscode搭建rust开发环境的图文教程

    Rust 是一种系统编程语言,它专注于内存安全、并发和性能,本文主要介绍了vscode搭建rust开发环境的图文教程,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • rust中间件actix_web在项目中的使用实战

    rust中间件actix_web在项目中的使用实战

    这篇文章主要介绍了rust中间件在项目中的使用实战,包括自定义中间件,日志中间件,Default headers,用户会话,错误处理的用法实例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • Rust 中判断两个 HashMap 是否相等

    Rust 中判断两个 HashMap 是否相等

    在Rust标准库中,HashMap 实现了 PartialEq 和 Eq trait,但是这些trait的实现是基于严格的结构相等性,包括元素的顺序,这篇文章主要介绍了Rust 中判断两个 HashMap 是否相等,需要的朋友可以参考下
    2024-04-04
  • Rust重载运算符之复数四则运算的实现

    Rust重载运算符之复数四则运算的实现

    这篇文章主要为大家详细介绍了Rust如何实现复数以及复数的四则运算,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下
    2023-08-08
  • rust 创建多线程web server的详细过程

    rust 创建多线程web server的详细过程

    web server 中主要的两个协议是 http 和 tcp,tcp 是底层协议,http 是构建在 tcp 之上的,本篇文章重点给大家介绍rust 创建多线程web server的详细过程,感兴趣的朋友跟随小编一起看看吧
    2023-11-11
  • 使用Rust语言编写一个ChatGPT桌面应用示例详解

    使用Rust语言编写一个ChatGPT桌面应用示例详解

    这篇文章主要介绍了如何用Rust编写一个ChatGPT桌面应用,文中有详细的流程介绍,对大家的学习或工作有意一定的帮助,需要的朋友可以参考下
    2023-05-05
  • Rust 中的文件操作示例详解

    Rust 中的文件操作示例详解

    Rust 中的路径操作是跨平台的,std::path 模块提供的了两个用于描述路径的类型,本文给大家介绍Rust 中的文件操作示例详解,感兴趣的朋友一起看看吧
    2021-11-11

最新评论