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

 更新时间:2022年09月29日 14:53:44   作者:古明地觉  
我们常常需要重复执行同一段代码,针对这种场景,Rust?提供了多种循环(loop)工具。一个循环会执行循环体中的代码直到结尾,并紧接着回到开头继续执行。而?Rust?提供了?3?种循环:loop、while?和?for,下面逐一讲解
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

楔子

我们常常需要重复执行同一段代码,针对这种场景,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中编写自定义Error的详细代码

    在Rust中编写自定义Error的详细代码

    Result<T, E> 类型可以方便地用于错误传导,Result<T, E>是模板类型,实例化后可以是各种类型,但 Rust 要求传导的 Result 中的 E 是相同类型的,所以我们需要编写自己的 Error 类型,本文给大家介绍了在Rust中编写自定义Error的详细代码,需要的朋友可以参考下
    2024-01-01
  • 探索Rust切片与Go有何区别

    探索Rust切片与Go有何区别

    这篇文章主要为大家介绍了Rust切片与Go的区别探索,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2024-01-01
  • 一文掌握Rust编程中的生命周期

    一文掌握Rust编程中的生命周期

    在Rust语言中, 每一个引用都有其生命周期, 通俗讲就是每个引用在程序执行的过程中都有其自身的作用域, 一旦离开其作用域, 其生命周期也宣告结束, 值不再有效,这篇文章主要介绍了Rust编程中的生命周期,需要的朋友可以参考下
    2023-11-11
  • rust生命周期详解

    rust生命周期详解

    生命周期是rust中用来规定引用的有效作用域,在大多数时候,无需手动声明,因为编译器能够自动推导,这篇文章主要介绍了rust生命周期相关知识,需要的朋友可以参考下
    2023-03-03
  • Rust 枚举和模式匹配的实现

    Rust 枚举和模式匹配的实现

    枚举是 Rust 中非常重要的复合类型,也是最强大的复合类型之一,广泛用于属性配置、错误处理、分支流程、类型聚合等场景中,本文就来介绍一下Rust 枚举和模式匹配,感兴趣的可以了解一下
    2023-12-12
  • Rust中into和from用法及区别介绍

    Rust中into和from用法及区别介绍

    这篇文章主要介绍了Rust中的?into和from使用及区别介绍,into和from是Rust语言中两个用于类型转换的函数,它们分别属于Into和From这两个trait,本文通过实例代码详细讲解,需要的朋友可以参考下
    2023-04-04
  • Rust Option类型基本使用详解

    Rust Option类型基本使用详解

    Rust的Option是一种强大的类型,用于处理可能为空的情况,避免了许多空值引起的运行时错误,本文介绍Rust Option类型详解,感兴趣的朋友一起看看吧
    2024-02-02
  • Rust+React创建富文本编辑器

    Rust+React创建富文本编辑器

    这篇文章主要为大家介绍了Rust+React创建富文本编辑器示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-07-07
  • Rust中字符串类型String的46种常用方法分享

    Rust中字符串类型String的46种常用方法分享

    Rust主要有两种类型的字符串:&str和String,本文主要为大家介绍的是String类型的字符串以及它常用的46种方法,感兴趣的小伙伴可以了解一下
    2023-06-06
  • 详解Rust 修改源

    详解Rust 修改源

    这篇文章主要介绍了Rust 修改源的相关知识,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2024-01-01

最新评论

?


http://www.vxiaotou.com