果然,連流程控制都這么另類
本文轉(zhuǎn)載自微信公眾號(hào)「polarisxu」,作者站長polaris。轉(zhuǎn)載本文請(qǐng)聯(lián)系polarisxu公眾號(hào)。
大家好,我是 polarisxu。
這是 Rust 勸退系列的第 7 個(gè)教程,探討 Rust 中的流程控制。注意,跟其他語言一樣,Rust 中有條件、循環(huán),但沒有 switch,而是有 match 模式匹配。
這個(gè)系列常規(guī)的標(biāo)題閱讀量實(shí)在有點(diǎn)看不下去,所以試試其他標(biāo)題。
01 運(yùn)算符
開始講解流程控制之前,先補(bǔ)充一個(gè)知識(shí)點(diǎn),那就是 Rust 的運(yùn)算符。
Rust 支持算術(shù)運(yùn)算符、關(guān)系運(yùn)算符、邏輯運(yùn)算符和位運(yùn)算符 4 種,它們和其他語言沒有什么不同,因此不細(xì)講了,只提醒一點(diǎn):Rust 中沒有自增(++)和自減運(yùn)算符(--)。
- Go 中 ++ 或 -- 是語句,只有一種形式:后綴,即 i++;而 C 等語言支持前綴和后綴,如 i++、++i。Rust 干脆全沒有。
吐槽:自增和自減運(yùn)算符,有時(shí)候挺好用的,Rust 為啥不支持呢?!(難道因?yàn)闆]有常規(guī)的 for 循環(huán),所以不需要?)
02 語句和表達(dá)式
為什么專門介紹語句和表達(dá)式?!
上文提到,Go 中的自增或自減是語句而不是表達(dá)式,這有什么不同呢?
很多語言對(duì)語句和表達(dá)式基本不會(huì)特意區(qū)分、強(qiáng)調(diào),所以很多人也不會(huì)在意這兩者有什么不同。但在 Rust 中,還是很有必要區(qū)分它們的。
實(shí)際上,Rust 中的語法可以分為兩大類:語句(Statement)和表達(dá)式(Expression)。語句是指要執(zhí)行的一些操作和產(chǎn)生副作用的表達(dá)式;而表達(dá)式主要用于計(jì)算求值。
語句通常分為聲明語句和表達(dá)式語句。像聲明各種語言項(xiàng),如變量、常量、結(jié)構(gòu)體、函數(shù)等,都是聲明語句:
- let a = 1;
- const PI: i32 = 3.14;
而表達(dá)式語句,指的是以分號(hào)結(jié)尾的表達(dá)式,一般會(huì)涉及到將多個(gè)表達(dá)式組合為語句。
《Rust 編程之道》上說,Rust 中的表達(dá)式一般分為「位置表達(dá)式」和「值表達(dá)式」,概念太多,容易勸退,直接按照其他語言的叫法:左值和右值。
之所以都用表達(dá)式的說法,是因?yàn)?Rust 中一切皆表達(dá)式。
羅里吧嗦講一堆,似乎沒啥用。知道有這么回事即可。只需要記住 Rust 中一切皆表達(dá)式即可。
03 條件表達(dá)式
知道為什么要強(qiáng)調(diào)「表達(dá)式」了嗎?一般語言中都叫:條件語句,但 Rust 中卻是條件表達(dá)式。
首先,條件表達(dá)式的語法和其他語言的條件語句類似,支持 if、else if、else 等,但它和 Go 中類似,條件默認(rèn)都不需要括號(hào)。但因?yàn)槭潜磉_(dá)式,所以它有返回值,而 Rust 是強(qiáng)類型語言,因此返回值的類型必須確定。比如以下代碼是能正常編譯的:
- fn testif() -> &'static str {
- let name = "polarisxu";
- if name == "polarisxu" {
- "Welcome"
- } else {
- "Forbidden"
- }
- }
看不懂沒關(guān)系。我們只關(guān)注 if-else 部分。
在塊表達(dá)式里(由 {} 包圍),直接一個(gè)字符串字面值(這是值表達(dá)式),連分號(hào)都沒有。Rust 沒有分號(hào)和 Go 中的沒有分號(hào)意義是不一樣的。
- Go 語句以分號(hào)結(jié)尾,分號(hào)推薦不寫,編譯器會(huì)自動(dòng)補(bǔ)上;
- Rust 語句必須手動(dòng)加分號(hào),如果不加分號(hào),那是一個(gè)表達(dá)式。
一切皆表達(dá)式,大括號(hào)包圍起來的是塊表達(dá)式,那塊表達(dá)式的值是什么?它的值是里面一系列表達(dá)式中最后一個(gè)表達(dá)式的值。
所以,上面的代碼,無論是執(zhí)行到 if 還是 else,整個(gè) if 表達(dá)式的值的類型是字符串。所以,以上代碼可以改為這樣:
- fn testif() -> &'static str {
- let name = "polarisxu";
- let result = if name == "polarisxu" {
- "Welcome"
- } else {
- "Forbidden"
- };
- hello
- }
我們將 if 表達(dá)式的結(jié)果保存在 result 變量中,注意 if 表達(dá)式大括號(hào)最后的分號(hào),這種情況,分號(hào)不能省略。
因此,在 if 表達(dá)式中,各個(gè)分支表達(dá)式最終的結(jié)果類型必須一致,否則編譯不通過。這也是為什么 Rust 不支持三元操作符 ?: 的原因。
if 是表達(dá)式有它的好處。在 Go 語言中,經(jīng)常會(huì)寫類似這樣的代碼:
- var result string
- if name == "polarisxu" {
- result = "Welcome"
- } else {
- result = "Forbidden"
- }
而 Rust 的代碼,result 少寫了很多次。但需要注意各分支結(jié)果類型的一致性。
if 表達(dá)式可以這么用,其他流程控制表達(dá)式也可以這么用。
特別說明一點(diǎn)。如果塊表達(dá)式的最后一個(gè)表達(dá)式是語句,比如:
- fn testif() -> &'static str {
- let name = "polarisxu";
- if name == "polarisxu" {
- "Welcome";
- } else {
- "forbidden";
- }
- }
這時(shí)編譯會(huì)報(bào)錯(cuò):mismatched types。
因?yàn)楹瘮?shù)要求返回值類型是 &str,而函數(shù)體最后返回的類型是空。這個(gè)空,在其他語言中一般是沒有返回值,或者是 void。但在 Rust 中,這個(gè)空是前面介紹類型時(shí)介紹過的「unit」類型,即 (),該類型有唯一的值,也是 ()。
所以,我們可以去掉函數(shù)的返回值,或者返回 ():
- fn testif() -> () {
- let name = "polarisxu";
- if name == "polarisxu" {
- "Welcome";
- } else {
- "forbidden";
- }
- }
很另類,有木有?!
04 循環(huán)表達(dá)式
Rust 中包含三種循環(huán)表達(dá)式:while、loop 和 for…in。其用法和其他編程語言相應(yīng)的語句類似。(注意,Go 中只有 for 一種循環(huán)語句)
loop 循環(huán)比較特殊,一般語言中沒有,它其實(shí)就是 while true {},相當(dāng)于 Go 中的 for {}。不得不說,還是 Go 簡單呀!
而 while 循環(huán),相當(dāng)于 Go 中的 for condition {},condition 為 true 時(shí),執(zhí)行循環(huán)體。
你發(fā)現(xiàn)沒,循環(huán)搞這么復(fù)雜,竟然沒有其他語言中普通的 for 循環(huán)?因?yàn)?for…in 可以搞定。
比如 Go 中的 for i := 0; i < 10; i++,在 Rust 中是這樣的:for i in 0..10 {}。來個(gè)簡單的例子,從 1 加到 100:
- let mut sum = 0;
- for i in 1..=100 {
- sum += i;
- }
- println!("1+2+..+100={}", sum);
小細(xì)節(jié):1..10 表示范圍 [1, 10),而 1..=10 表示范圍 [1, 10]
最后,和其他語言一樣,循環(huán)支持 continue 和 break 語句。
05 小結(jié)
Rust 中一切皆表達(dá)式,當(dāng)某個(gè)地方需要一個(gè)表達(dá)式,但卻是一個(gè)語句時(shí),編譯器會(huì)自動(dòng)補(bǔ)上單元值,即 (),這算是一個(gè)特殊的表達(dá)式。
雖然控制結(jié)構(gòu),if、循環(huán)等都是表達(dá)式,為了不搞特殊化(畢竟大家習(xí)慣很多其他語言,特殊化可能容易把自己搞迷糊),建議大家盡量別把它們當(dāng)表達(dá)式看待,很其他語言一樣正常寫,該有分號(hào)的加分號(hào)。
不過,如果是 Go 程序員寫 Rust,很可能忘記分號(hào)。而 Rust 中,有時(shí)候有分號(hào)和沒有分號(hào)都能編譯,但意思可能變了,這個(gè)要特別注意。(PHPer 表示,經(jīng)常在 PHP 和 Go 之間切換時(shí),分號(hào)的問題很糾結(jié),有木有?!)
控制流程中的模式匹配,下節(jié)再講!