日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

詳解Rust中三種循環(loop,while,for)的使用_Rust語言

作者:古明地覺 ? 更新時間: 2022-11-18 編程語言

楔子

我們常常需要重復執行同一段代碼,針對這種場景,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
?????*/
}

代碼是不是更加精煉了呢。

原文鏈接:https://mp.weixin.qq.com/s/5oag57k5gn6ADUb_c59_Mg

欄目分類
最近更新