<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          半小時(shí)入門 Rust?是的,你沒(méi)看錯(cuò)

          共 25972字,需瀏覽 52分鐘

           ·

          2021-12-09 13:12

          對(duì) Rust 稍微了解的朋友,對(duì)其學(xué)習(xí)難度應(yīng)該有所耳聞。但今天這篇文章,作者試圖讓新手半小時(shí)入門 Rust。你信嗎?要不試試?

          Rust 是一門系統(tǒng)編程語(yǔ)言,專注于安全:包括內(nèi)存安全、并發(fā)安全等。它支持函數(shù)式和命令式以及泛型等多種編程范式。

          在這篇文章中,作者并不關(guān)注于 1 個(gè)或幾個(gè)關(guān)鍵概念,相反他希望通過(guò)代碼塊縱覽 Rust 的各種特性,包括各種關(guān)鍵詞與標(biāo)識(shí)符的意義。多讀代碼,通過(guò)代碼學(xué)習(xí)。(螃蟹哥建議:別只是讀,動(dòng)手敲代碼更重要)

          01 變量

          先介紹 let 變量綁定:(注意,Rust 中一般稱之為綁定)

          let?x;??//?declare?"x"
          x?=?42;?//?assign?42?to?"x"

          你也可以寫成一行:

          let?x?=?42;

          通常,Rust 能夠自動(dòng)推斷出變量的類型,但你可以使用 : 來(lái)顯示指定變量的數(shù)據(jù)類型:(有時(shí)候必須顯示指定)

          let?x:?i32;?//?`i32`?is?a?signed?32-bit?integer
          x?=?42;

          //?there's?i8,?i16,?i32,?i64,?i128
          //????also?u8,?u16,?u32,?u64,?u128?for?unsigned

          同樣的,你也可以寫成一行(一般建議這么寫):

          let?x:?i32?=?42;

          如果你聲明一個(gè)變量并在初始化之前就調(diào)用它,編譯器會(huì)報(bào)錯(cuò):

          let?x;
          foobar(x);?//?error:?borrow?of?possibly-uninitialized?variable:?`x`
          x?=?42;

          然而,這樣做完全沒(méi)問(wèn)題:

          let?x;
          x?=?42;
          foobar(x);?//?the?type?of?`x`?will?be?inferred?from?here

          下劃線 _ 是一個(gè)特殊的變量名,或者更確切地說(shuō)是「名稱的缺失」。它的基本意思是扔掉,可以理解為垃圾桶:(和 Go 中的 _ 差不多)

          //?this?does?*nothing*?because?42?is?a?constant
          let?_?=?42;

          //?this?calls?`get_thing`?but?throws?away?its?result
          let?_?=?get_thing();

          以下劃線開頭的變量名是常規(guī)名稱,只是編譯器不會(huì)警告它們未被使用:

          //?we?may?use?`_x`?eventually,?but?our?code?is?a?work-in-progress
          //?and?we?just?wanted?to?get?rid?of?a?compiler?warning?for?now.
          let?_x?=?42;

          可以引入具有相同名稱的另一個(gè)綁定——這相當(dāng)于隱藏前一個(gè)變量綁定:

          let?x?=?13;
          let?x?=?x?+?3;
          //?using?`x`?after?that?line?only?refers?to?the?second?`x`,
          //?the?first?`x`?no?longer?exists.

          Rust 有元組類型,你可以將其看作是“不同類型值的定長(zhǎng)集合”。

          let?pair?=?('a',?17);
          pair.0;?//?this?is?'a'
          pair.1;?//?this?is?17

          如果真的想顯示指定 pair 的數(shù)據(jù)類型,可以這么寫:

          let?pair:?(char,?i32)?=?('a',?17);

          元組在賦值時(shí)可以被拆解,也就是它們可以被分解成各自的字段:

          let?(some_char,?some_int)?=?('a',?17);
          //?now,?`some_char`?is?'a',?and?`some_int`?is?17

          一個(gè)函數(shù)可以返還一個(gè)元組,這類似于多返回值:

          let?(left,?right)?=?slice.split_at(middle);

          當(dāng)然,在解構(gòu)一個(gè)元組時(shí),可以只分離它的一部分,這就用到了 _

          let?(_,?right)?=?slice.split_at(middle);

          分號(hào)表示語(yǔ)句的結(jié)尾:

          let?x?=?3;
          let?y?=?5;
          let?z?=?y?+?x;

          不加分號(hào)意味著語(yǔ)句可以跨多行:(這些是什么意思,稍后解釋)

          let?x?=?vec![1,?2,?3,?4,?5,?6,?7,?8]
          ????.iter()
          ????.map(|x|?x?+?3)
          ????.fold(0,?|x,?y|?x?+?y);

          02 函數(shù)

          fn 用來(lái)聲明一個(gè)函數(shù)。下面是一個(gè) void 函數(shù)(沒(méi)有參數(shù),沒(méi)有返回值):

          fn?greet()?{
          ????println!("Hi?there!");
          }

          這是一個(gè)返回 32 位帶符號(hào)整數(shù)類型的函數(shù)。箭頭表示它的返回類型:

          fn?fair_dice_roll()?->?i32?{
          ????4
          }

          花括號(hào)表示一個(gè)代碼塊,且擁有其自己的作用域:

          //?This?prints?"in",?then?"out"
          fn?main()?{
          ????let?x?=?"out";
          ????{
          ????????//?this?is?a?different?`x`
          ????????let?x?=?"in";
          ????????println!("{}",?x);
          ????}
          ????println!("{}",?x);
          }

          代碼塊也是表示式,最后一個(gè)表達(dá)式的值是整個(gè)代碼塊的值,以下代碼表達(dá)的意思一樣:

          //?this:
          let?x?=?42;

          //?is?equivalent?to?this:
          let?x?=?{?42?};

          在一個(gè)代碼塊中,可以有多個(gè)語(yǔ)句:

          let?x?=?{
          ????let?y?=?1;?//?first?statement
          ????let?z?=?2;?//?second?statement
          ????y?+?z?//?this?is?the?*tail*?-?what?the?whole?block?will?evaluate?to
          };

          這也是為什么“省略函數(shù)末尾的分號(hào)”等同于加上了 retrun,以下是等價(jià)的:

          fn?fair_dice_roll()?->?i32?{
          ????return?4;
          }

          fn?fair_dice_roll()?->?i32?{
          ????4
          }

          if 條件語(yǔ)句也是表達(dá)式:

          fn?fair_dice_roll()?->?i32?{
          ????if?feeling_lucky?{
          ????????6
          ????}?else?{
          ????????4
          ????}
          }

          match 也是一個(gè)表達(dá)式:

          fn?fair_dice_roll()?->?i32?{
          ????match?feeling_lucky?{
          ????????true?=>?6,
          ????????false?=>?4,
          ????}
          }

          Dots(點(diǎn)號(hào)) 通常用于訪問(wèn)某個(gè)對(duì)象的字段:

          let?a?=?(10,?20);
          a.0;?//?this?is?10

          let?amos?=?get_some_struct();
          amos.nickname;?//?this?is?"fasterthanlime"

          或者調(diào)用對(duì)象(值)的方法:

          let?nick?=?"fasterthanlime";
          nick.len();?//?this?is?14

          雙冒號(hào)(::)與此類似,但它是對(duì)命名空間進(jìn)行操作。

          在這個(gè)例子中,std 是一個(gè) crate (~ a library),cmp 是一個(gè) module(~ a source file),而 min 是個(gè)函數(shù):

          let?least?=?std::cmp::min(3,?8);?//?this?is?3

          use 指令可用于從其他命名空間中“引入作用域(scope)”名稱:

          use?std::cmp::min;

          let?least?=?min(7,?1);?//?this?is?1

          在 use 指令中,花括號(hào)還有另一個(gè)含義:“globs”。如果我們想同時(shí)導(dǎo)入 min 和 max,可以這么做:

          //?this?works:
          use?std::cmp::min;
          use?std::cmp::max;

          //?this?also?works:
          use?std::cmp::{min,?max};

          //?this?also?works!
          use?std::{cmp::min,?cmp::max};

          通配符(*)允許從命名空間導(dǎo)入符號(hào):

          //?this?brings?`min`?and?`max`?in?scope,?and?many?other?things
          use?std::cmp::*;

          類型也相當(dāng)于命名空間,通過(guò) :: 調(diào)用其上的方法,以下兩種方式等價(jià):

          let?x?=?"amos".len();?//?this?is?4
          let?x?=?str::len("amos");?//?this?is?also?4

          str 是基本數(shù)據(jù)類型(primitive type),但在默認(rèn)情況下,許多非基本數(shù)據(jù)類型也在當(dāng)前作用域中(即不需要手動(dòng) use 導(dǎo)入)。

          //?`Vec`?is?a?regular?struct,?not?a?primitive?type
          let?v?=?Vec::new();

          //?this?is?exactly?the?same?code,?but?with?the?*full*?path?to?`Vec`
          let?v?=?std::vec::Vec::new();

          為什么可以這么做呢?為了方便,Rust 在每個(gè)模塊的開頭都插入了以下代碼(我認(rèn)為類似 Java 默認(rèn)導(dǎo)入 java.lang 包):

          use?std::prelude::v1::*;

          以上代碼會(huì)以此導(dǎo)出許多標(biāo)識(shí)符,比如 Vec、String、Option、Result。

          03 結(jié)構(gòu)體

          使用 struct 關(guān)鍵字聲明結(jié)構(gòu)體:

          struct?Vec2?{
          ????x:?f64,?//?64-bit?floating?point,?aka?"double?precision"
          ????y:?f64,
          }

          可以使用結(jié)構(gòu)語(yǔ)句初始化:

          let?v1?=?Vec2?{?x:?1.0,?y:?3.0?};
          let?v2?=?Vec2?{?y:?2.0,?x:?4.0?};
          //?the?order?does?not?matter,?only?the?names?do

          有一個(gè)快捷方式可以從另一個(gè)結(jié)構(gòu)體初始化本結(jié)構(gòu)體的其余字段:

          let?v3?=?Vec2?{
          ????x:?14.0,
          ????..v2
          };

          這就是所謂的“結(jié)構(gòu)更新語(yǔ)法”,只能發(fā)生在最后一個(gè)位置,而且不能在其后面再跟一個(gè)逗號(hào)。

          注意其余字段可以表示所有字段:

          let?v4?=?Vec2?{?..v3?};

          結(jié)構(gòu)體與元組一樣,可以被解構(gòu)。就像是一個(gè)有效的 let 模式:

          let?(left,?right)?=?slice.split_at(middle);

          let?v?=?Vec2?{?x:?3.0,?y:?6.0?};
          let?Vec2?{?x,?y?}?=?v;
          //?`x`?is?now?3.0,?`y`?is?now?`6.0`

          還可以這樣:

          let?Vec2?{?x,?..?}?=?v;
          //?this?throws?away?`v.y`

          let 模式可以作為 if 的條件使用:

          struct?Number?{
          ????odd:?bool,
          ????value:?i32,
          }

          fn?main()?{
          ????let?one?=?Number?{?odd:?true,?value:?1?};
          ????let?two?=?Number?{?odd:?false,?value:?2?};
          ????print_number(one);
          ????print_number(two);
          }

          fn?print_number(n:?Number)?{
          ????if?let?Number?{?odd:?true,?value?}?=?n?{
          ????????println!("Odd?number:?{}",?value);
          ????}?else?if?let?Number?{?odd:?false,?value?}?=?n?{
          ????????println!("Even?number:?{}",?value);
          ????}
          }

          //?this?prints:
          //?Odd?number:?1
          //?Even?number:?2

          多分支的 match 也是一種模式,就像 if let:

          fn?print_number(n:?Number)?{
          ????match?n?{
          ????????Number?{?odd:?true,?value?}?=>?println!("Odd?number:?{}",?value),
          ????????Number?{?odd:?false,?value?}?=>?println!("Even?number:?{}",?value),
          ????}
          }

          //?this?prints?the?same?as?before

          match 必須是囊括所有情況的的:至少需要匹配一個(gè)分支(Rust 中叫做 arm)。

          fn?print_number(n:?Number)?{
          ????match?n?{
          ????????Number?{?value:?1,?..?}?=>?println!("One"),
          ????????Number?{?value:?2,?..?}?=>?println!("Two"),
          ????????Number?{?value,?..?}?=>?println!("{}",?value),
          ????????//?if?that?last?arm?didn't?exist,?we?would?get?a?compile-time?error
          ????}
          }

          如果實(shí)在沒(méi)法包含所有情況,可以增加一個(gè) _ 來(lái)捕獲所有其他情況,類似其他語(yǔ)言 switch 的 default:

          fn?print_number(n:?Number)?{
          ????match?n.value?{
          ????????1?=>?println!("One"),
          ????????2?=>?println!("Two"),
          ????????_?=>?println!("{}",?n.value),
          ????}
          }

          你可以在自定義類型上聲明方法:

          struct?Number?{
          ????odd:?bool,
          ????value:?i32,
          }

          impl?Number?{
          ????fn?is_strictly_positive(self)?->?bool?{
          ????????self.value?>?0
          ????}
          }

          然后像平常一樣使用:

          fn?main()?{
          ????let?minus_two?=?Number?{
          ????????odd:?false,
          ????????value:?-2,
          ????};
          ????println!("positive??{}",?minus_two.is_strictly_positive());
          ????//?this?prints?"positive??false"
          }

          默認(rèn)情況下,聲明變量后它是不可變的,比如以下 odd 不能被重新賦值:

          fn?main()?{
          ????let?n?=?Number?{
          ????????odd:?true,
          ????????value:?17,
          ????};
          ????n.odd?=?false;?//?error:?cannot?assign?to?`n.odd`,
          ???????????????????//?as?`n`?is?not?declared?to?be?mutable
          }

          整個(gè)結(jié)構(gòu)體值也不能二次賦值:

          fn?main()?{
          ????let?n?=?Number?{
          ????????odd:?true,
          ????????value:?17,
          ????};
          ????n?=?Number?{
          ????????odd:?false,
          ????????value:?22,
          ????};?//?error:?cannot?assign?twice?to?immutable?variable?`n`
          }

          關(guān)鍵字 mut 可以將變量聲明變?yōu)榭勺兊模?/p>

          fn?main()?{
          ????let?mut?n?=?Number?{
          ????????odd:?true,
          ????????value:?17,
          ????}
          ????n.value?=?19;?//?all?good
          }

          04 trait(特征)

          Traits(特征) 描述的是多種數(shù)據(jù)類型的共有的東西:(可以類比為其他語(yǔ)言的接口)

          trait?Signed?{
          ????fn?is_strictly_negative(self)?->?bool;
          }

          我們可以這么實(shí)現(xiàn):

          • 可以對(duì)外部類型實(shí)現(xiàn)自定義的 trait;
          • 可以對(duì)自定義類型實(shí)現(xiàn)外部 trait;
          • 不允許對(duì)外部類型實(shí)現(xiàn)外部 trait;

          這些被稱為“孤兒法則”。你如果有點(diǎn)暈,進(jìn)一步解釋一下:

          當(dāng)你為某類型實(shí)現(xiàn)某 trait 的時(shí)候,必須要求類型或者 trait 至少有一個(gè)是在當(dāng)前 crate 中定義的。你不能為第三方的類型實(shí)現(xiàn)第三方的 trait。

          此外,這時(shí)記得讓 trait 的方法可訪問(wèn)(公開)。

          可以在我們上面定義的類型(Number)上實(shí)現(xiàn) trait:

          impl?Signed?for?Number?{
          ????fn?is_strictly_negative(self)?->?bool?{
          ????????self.value?0
          ????}
          }

          fn?main()?{
          ????let?n?=?Number?{?odd:?false,?value:?-44?};
          ????println!("{}",?n.is_strictly_negative());?//?prints?"true"
          }

          我們對(duì)外部類型(foreign type)實(shí)現(xiàn)我們定義的 trait:(這里的外部類型使用了基本類型 i32)

          impl?Signed?for?i32?{
          ????fn?is_strictly_negative(self)?->?bool?{
          ????????self?0
          ????}
          }

          fn?main()?{
          ????let?n:?i32?=?-44;
          ????println!("{}",?n.is_strictly_negative());?//?prints?"true"
          }

          接著在我們定義的類型上實(shí)現(xiàn)一個(gè)外部 trait:

          //?the?`Neg`?trait?is?used?to?overload?`-`,?the
          //?unary?minus?operator.
          impl?std::ops::Neg?for?Number?{
          ????type?Output?=?Number;

          ????fn?neg(self)?->?Number?{
          ????????Number?{
          ????????????value:?-self.value,
          ????????????odd:?self.odd,
          ????????}????????
          ????}
          }

          fn?main()?{
          ????let?n?=?Number?{?odd:?true,?value:?987?};
          ????let?m?=?-n;?//?this?is?only?possible?because?we?implemented?`Neg`
          ????println!("{}",?m.value);?//?prints?"-987"
          }

          impl 代碼塊通常會(huì)對(duì)應(yīng)一個(gè)類型,所以在 impl 內(nèi),Self 就表示該類型:

          impl?std::ops::Neg?for?Number?{
          ????type?Output?=?Self;

          ????fn?neg(self)?->?Self?{
          ????????Self?{
          ????????????value:?-self.value,
          ????????????odd:?self.odd,
          ????????}
          ????}
          }

          有一些 trait 只是作為標(biāo)記:它們并不是說(shuō)某個(gè)類型實(shí)現(xiàn)了某些方法,只是表明某些東西能通過(guò)類型完成。例如,i32 實(shí)現(xiàn)了 Copy trait,那么以下代碼就是可行的:(即 i32 是一個(gè) Copy)

          fn?main()?{
          ????let?a:?i32?=?15;
          ????let?b?=?a;?//?`a`?is?copied
          ????let?c?=?a;?//?`a`?is?copied?again
          }

          下面的代碼也是能運(yùn)行的:

          fn?print_i32(x:?i32)?{
          ????println!("x?=?{}",?x);
          }

          fn?main()?{
          ????let?a:?i32?=?15;
          ????print_i32(a);?//?`a`?is?copied
          ????print_i32(a);?//?`a`?is?copied?again
          }

          但是 Number 的結(jié)構(gòu)體并不是一個(gè) Copy,所以下面的代碼會(huì)報(bào)錯(cuò):

          fn?main()?{
          ????let?n?=?Number?{?odd:?true,?value:?51?};
          ????let?m?=?n;?//?`n`?is?moved?into?`m`
          ????let?o?=?n;?//?error:?use?of?moved?value:?`n`
          }

          同樣下面的代碼也不會(huì)正常:

          fn?print_number(n:?Number)?{
          ????println!("{}?number?{}",?if?n.odd?{?"odd"?}?else?{?"even"?},?n.value);
          }

          fn?main()?{
          ????let?n?=?Number?{?odd:?true,?value:?51?};
          ????print_number(n);?//?`n`?is?moved
          ????print_number(n);?//?error:?use?of?moved?value:?`n`
          }

          但如果 print _ number 采用不可變引用,那么它就可以正常工作:

          fn?print_number(n:?&Number)?{
          ????println!("{}?number?{}",?if?n.odd?{?"odd"?}?else?{?"even"?},?n.value);
          }

          fn?main()?{
          ????let?n?=?Number?{?odd:?true,?value:?51?};
          ????print_number(&n);?//?`n`?is?borrowed?for?the?time?of?the?call
          ????print_number(&n);?//?`n`?is?borrowed?again
          }

          如果一個(gè)函數(shù)接受一個(gè)可變的引用,它也可以工作,但前提是我們的變量綁定也是可變的。

          fn?invert(n:?&mut?Number)?{
          ????n.value?=?-n.value;
          }

          fn?print_number(n:?&Number)?{
          ????println!("{}?number?{}",?if?n.odd?{?"odd"?}?else?{?"even"?},?n.value);
          }

          fn?main()?{
          ????//?this?time,?`n`?is?mutable
          ????let?mut?n?=?Number?{?odd:?true,?value:?51?};
          ????print_number(&n);
          ????invert(&mut?n);?//?`n?is?borrowed?mutably?-?everything?is?explicit
          ????print_number(&n);
          }

          trait 方法也可以通過(guò) self 進(jìn)行引用或可變引用:

          impl?std::clone::Clone?for?Number?{
          ????fn?clone(&self)?->?Self?{
          ????????Self?{?..*self?}
          ????}
          }

          當(dāng)調(diào)用 trait 方法時(shí),接收方隱式地被借用:

          fn?main()?{
          ????let?n?=?Number?{?odd:?true,?value:?51?};
          ????let?mut?m?=?n.clone();
          ????m.value?+=?100;
          ????
          ????print_number(&n);
          ????print_number(&m);
          }

          沒(méi)理解什么意思?以下是等價(jià)的,你該理解了:

          let?m?=?n.clone();

          let?m?=?std::clone::Clone::clone(&n);

          像 Copy 這樣的標(biāo)記特征沒(méi)有方法:

          //?note:?`Copy`?requires?that?`Clone`?is?implemented?too
          impl?std::clone::Clone?for?Number?{
          ????fn?clone(&self)?->?Self?{
          ????????Self?{?..*self?}
          ????}
          }

          impl?std::marker::Copy?for?Number?{}

          現(xiàn)在 Clone 仍然正常:

          fn?main()?{
          ????let?n?=?Number?{?odd:?true,?value:?51?};
          ????let?m?=?n.clone();
          ????let?o?=?n.clone();
          }

          但是 Number 值將不再被移動(dòng):

          fn?main()?{
          ????let?n?=?Number?{?odd:?true,?value:?51?};
          ????let?m?=?n;?//?`m`?is?a?copy?of?`n`
          ????let?o?=?n;?//?same.?`n`?is?neither?moved?nor?borrowed.
          }

          有些特性非常常見(jiàn),它們可以通過(guò) derive 屬性自動(dòng)實(shí)現(xiàn):

          #[derive(Clone,?Copy)]
          struct?Number?{
          ????odd:?bool,
          ????value:?i32,
          }

          //?this?expands?to?`impl?Clone?for?Number`?and?`impl?Copy?for?Number`?blocks.

          05 泛型

          函數(shù)支持泛型:

          fn?foobar(arg:?T)?{
          ????//?do?something?with?`arg`
          }

          它們可以有多個(gè)類型參數(shù),這些參數(shù)可以在函數(shù)的聲明和函數(shù)體中使用,而不是具體類型:

          fn?foobar(left:?L,?right:?R)?{
          ????//?do?something?with?`left`?and?`right`
          }

          類型參數(shù)通常有約束,因此你可以實(shí)際使用它們。

          最簡(jiǎn)單的限制是特征(trait)名:

          fn?print(value:?T)?{
          ????println!("value?=?{}",?value);
          }

          fn?printDebug>(value:?T)?{
          ????println!("value?=?{:?}",?value);
          }

          類型參數(shù)約束有一個(gè)更長(zhǎng)的語(yǔ)法:

          fn?print(value:?T)
          where
          ????T:?Display,
          {
          ????println!("value?=?{}",?value);
          }

          約束可能更復(fù)雜:它們可能需要一個(gè)類型參數(shù)來(lái)實(shí)現(xiàn)多個(gè) traits:

          use?std::fmt::Debug;

          fn?compare(left:?T,?right:?T)
          where
          ????T:?Debug?+?PartialEq,
          {
          ????println!("{:?}?{}?{:?}",?left,?if?left?==?right?{?"=="?}?else?{?"!="?},?right);
          }

          fn?main()?{
          ????compare("tea",?"coffee");
          ????//?prints:?"tea"?!=?"coffee"
          }

          泛型函數(shù)可以看作是命名空間,包含無(wú)數(shù)具有不同具體類型的函數(shù)。

          與 crates、modules 和 類型一樣,泛型函數(shù)可以通過(guò) :: 指定具體類型:

          fn?main()?{
          ????use?std::any::type_name;
          ????println!("{}",?type_name::<i32>());?//?prints?"i32"
          ????println!("{}",?type_name::<(f64,?char)>());?//?prints?"(f64,?char)"
          }

          這就是所謂的 turbofish 語(yǔ)法,因?yàn)?::<> 看起來(lái)像一條魚。

          結(jié)構(gòu)體也可以是泛型:

          struct?Pair?{
          ????a:?T,
          ????b:?T,
          }

          fn?print_type_name(_val:?&T)?{
          ????println!("{}",?std::any::type_name::());
          }

          fn?main()?{
          ????let?p1?=?Pair?{?a:?3,?b:?9?};
          ????let?p2?=?Pair?{?a:?true,?b:?false?};
          ????print_type_name(&p1);?//?prints?"Pair"
          ????print_type_name(&p2);?//?prints?"Pair"
          }

          標(biāo)準(zhǔn)庫(kù)類型 Vec(堆分配數(shù)組)是泛型的:

          fn?main()?{
          ????let?mut?v1?=?Vec::new();
          ????v1.push(1);
          ????let?mut?v2?=?Vec::new();
          ????v2.push(false);
          ????print_type_name(&v1);?//?prints?"Vec"
          ????print_type_name(&v2);?//?prints?"Vec"
          }

          06 宏

          說(shuō)到 Vec,它附帶了一個(gè)宏 “Vec 字面值”。

          fn?main()?{
          ????let?v1?=?vec![1,?2,?3];
          ????let?v2?=?vec![true,?false,?true];
          ????print_type_name(&v1);?//?prints?"Vec"
          ????print_type_name(&v2);?//?prints?"Vec"
          }

          所有的 name!(),name![] 或 name!{} 都是在調(diào)用宏。宏最終會(huì)展開為常規(guī)代碼。

          所以,println 也是一個(gè)宏。

          fn?main()?{
          ????println!("{}",?"Hello?there!");
          }

          這個(gè)會(huì)展開為和下面有相同效果的代碼:

          fn?main()?{
          ????use?std::io::{self,?Write};
          ????io::stdout().lock().write_all(b"Hello?there!\n").unwrap();
          }

          panic 也是一個(gè)宏。如果調(diào)用了它,會(huì)使用錯(cuò)誤消息以及錯(cuò)誤的文件名/行號(hào)強(qiáng)制停止執(zhí)行:

          fn?main()?{
          ????panic!("This?panics");
          }
          //?output:?thread?'main'?panicked?at?'This?panics',?src/main.rs:3:5

          有些方法也會(huì)引起 panic。例如,Option 類型可以包含某些內(nèi)容,也可以不包含任何內(nèi)容。如果調(diào)用 Unwrap(),但它不包含任何內(nèi)容,就會(huì) panic:

          fn?main()?{
          ????let?o1:?Option<i32>?=?Some(128);
          ????o1.unwrap();?//?this?is?fine

          ????let?o2:?Option<i32>?=?None;
          ????o2.unwrap();?//?this?panics!
          }

          //?output:?thread?'main'?panicked?at?'called?`Option::unwrap()`?on?a?`None`?value',?src/libcore/option.rs:378:21

          07 枚舉

          Option 不是結(jié)構(gòu)體,而是帶有兩個(gè)變體的枚舉:

          enum?Option?{
          ????None,
          ????Some(T),
          }

          impl?Option?{
          ????fn?unwrap(self)?->?T?{
          ????????//?enums?variants?can?be?used?in?patterns:
          ????????match?self?{
          ????????????Self::Some(t)?=>?t,
          ????????????Self::None?=>?panic!(".unwrap()?called?on?a?None?option"),
          ????????}
          ????}
          }

          use?self::Option::{None,?Some};

          fn?main()?{
          ????let?o1:?Option<i32>?=?Some(128);
          ????o1.unwrap();?//?this?is?fine

          ????let?o2:?Option<i32>?=?None;
          ????o2.unwrap();?//?this?panics!
          }

          //?output:?thread?'main'?panicked?at?'.unwrap()?called?on?a?None?option',?src/main.rs:11:27

          Result 也是一個(gè)枚舉,它可以包含一些東西,或包含一個(gè)錯(cuò)誤:

          enum?Result?{
          ????Ok(T),
          ????Err(E),
          }

          如果 Result 包含錯(cuò)誤時(shí),進(jìn)行 unwrap 操作會(huì) panic。

          08 生命周期

          變量綁定有一個(gè)“生命周期(lifetime)”:

          fn?main()?{
          ????//?`x`?doesn't?exist?yet
          ????{
          ????????let?x?=?42;?//?`x`?starts?existing
          ????????println!("x?=?{}",?x);
          ????????//?`x`?stops?existing
          ????}
          ????//?`x`?no?longer?exists
          }

          類似地,引用也有生命周期:

          fn?main()?{
          ????//?`x`?doesn't?exist?yet
          ????{
          ????????let?x?=?42;?//?`x`?starts?existing
          ????????let?x_ref?=?&x;?//?`x_ref`?starts?existing?-?it?borrows?`x`
          ????????println!("x_ref?=?{}",?x_ref);
          ????????//?`x_ref`?stops?existing
          ????????//?`x`?stops?existing
          ????}
          ????//?`x`?no?longer?exists
          }

          引用的生命周期你不能超過(guò)其借用的變量綁定的生命周期:

          fn?main()?{
          ????let?x_ref?=?{
          ????????let?x?=?42;
          ????????&x
          ????};
          ????println!("x_ref?=?{}",?x_ref);
          ????//?error:?`x`?does?not?live?long?enough
          }

          一個(gè)變量綁定可以被多次借用:

          fn?main()?{
          ????let?x?=?42;
          ????let?x_ref1?=?&x;
          ????let?x_ref2?=?&x;
          ????let?x_ref3?=?&x;
          ????println!("{}?{}?{}",?x_ref1,?x_ref2,?x_ref3);
          }

          在被借用期間,變量綁定不能被修改:

          fn?main()?{
          ????let?mut?x?=?42;
          ????let?x_ref?=?&x;
          ????x?=?13;
          ????println!("x_ref?=?{}",?x_ref);
          ????//?error:?cannot?assign?to?`x`?because?it?is?borrowed
          }

          當(dāng)存在不可變借用時(shí),變量不能再次作為可變借用。

          fn?main()?{
          ????let?mut?x?=?42;
          ????let?x_ref1?=?&x;
          ????let?x_ref2?=?&mut?x;
          ????//?error:?cannot?borrow?`x`?as?mutable?because?it?is?also?borrowed?as?immutable
          ????println!("x_ref1?=?{}",?x_ref1);
          }

          函數(shù)參數(shù)中的引用也有生命周期:

          fn?print(x:?&i32)?{
          ????//?`x`?is?borrowed?(from?the?outside)?for?the
          ????//?entire?time?this?function?is?called.
          }

          帶有引用參數(shù)的函數(shù)可以使用具有不同生命期的 borrows 來(lái)調(diào)用,所以:

          • 所有參數(shù)是引用的函數(shù),它們實(shí)際上都是泛型函數(shù);
          • 生命周期就是泛型參數(shù);

          生命周期名以單引號(hào) ' 開頭:

          //?elided?(non-named)?lifetimes:
          fn?print(x:?&i32)?{}

          //?named?lifetimes:
          fn?print<'a>(x:?&'a?i32)?{}

          這允許返回的引用的生命周期依賴于參數(shù)的生命周期:

          struct?Number?{
          ????value:?i32,
          }

          fn?number_value<'a>(num:?&'a?Number)?->?&'a?i32?{
          ????&num.value
          }

          fn?main()?{
          ????let?n?=?Number?{?value:?47?};
          ????let?v?=?number_value(&n);
          ????//?`v`?borrows?`n`?(immutably),?thus:?`v`?cannot?outlive?`n`.
          ????//?While?`v`?exists,?`n`?cannot?be?mutably?borrowed,?mutated,?moved,?etc.
          }

          當(dāng)只有一個(gè)輸入生命周期時(shí),它不需要命名,所有的東西都具有相同的生命周期,因此下面的兩個(gè)函數(shù)是等價(jià)的:

          fn?number_value<'a>(num:?&'a?Number)?->?&'a?i32?{
          ????&num.value
          }

          fn?number_value(num:?&Number)?->?&i32?{
          ????&num.value
          }

          當(dāng)有引用字段時(shí),結(jié)構(gòu)體也是基于生命周期的泛型:

          struct?NumRef<'a>?{
          ????x:?&'a?i32,
          }

          fn?main()?{
          ????let?x:?i32?=?99;
          ????let?x_ref?=?NumRef?{?x:?&x?};
          ????//?`x_ref`?cannot?outlive?`x`,?etc.
          }

          同樣的代碼,但是有一個(gè)額外的函數(shù):

          struct?NumRef<'a>?{
          ????x:?&'a?i32,
          }

          fn?as_num_ref<'a>(x:?&'a?i32)?->?NumRef<'a>?{
          ????NumRef?{?x:?&x?}
          }

          fn?main()?{
          ????let?x:?i32?=?99;
          ????let?x_ref?=?as_num_ref(&x);
          ????//?`x_ref`?cannot?outlive?`x`,?etc.
          }

          相同的代碼,但省略了生命期:

          struct?NumRef<'a>?{
          ????x:?&'a?i32,
          }

          fn?as_num_ref(x:?&i32)?->?NumRef<'_>?{
          ????NumRef?{?x:?&x?}
          }

          fn?main()?{
          ????let?x:?i32?=?99;
          ????let?x_ref?=?as_num_ref(&x);
          ????//?`x_ref`?cannot?outlive?`x`,?etc.
          }

          Impl 塊也可以是基于生命周期的泛型:

          impl<'a>?NumRef<'a>?{
          ????fn?as_i32_ref(&'a?self)?->?&'a?i32?{
          ????????self.x
          ????}
          }

          fn?main()?{
          ????let?x:?i32?=?99;
          ????let?x_num_ref?=?NumRef?{?x:?&x?};
          ????let?x_i32_ref?=?x_num_ref.as_i32_ref();
          ????//?neither?ref?can?outlive?`x`
          }

          但你也可以在這里省略:

          impl<'a>?NumRef<'a>?{
          ????fn?as_i32_ref(&self)?->?&i32?{
          ????????self.x
          ????}
          }

          如果你從來(lái)不需要這個(gè)名字,你可以省去更多:

          impl?NumRef<'_>?{
          ????fn?as_i32_ref(&self)?->?&i32?{
          ????????self.x
          ????}
          }

          有一個(gè)特殊的生命周期,名為 'static,它對(duì)整個(gè)程序的生命周期都有效。

          字符串字面量就是 'static

          struct?Person?{
          ????name:?&'static?str,
          }

          fn?main()?{
          ????let?p?=?Person?{
          ????????name:?"fasterthanlime",
          ????};
          }

          但非字面量字符串并不是 static 的:

          struct?Person?{
          ????name:?&'static?str,
          }

          fn?main()?{
          ????let?name?=?format!("fasterthan{}",?"lime");
          ????let?p?=?Person?{?name:?&name?};
          ????//?error:?`name`?does?not?live?long?enough
          }

          在最后一個(gè)示例中,局部變量 name 并不是 ?&'static str,而是 String。它是動(dòng)態(tài)分配的,并且會(huì)在不需要釋放。它的生命周期小于整個(gè)程序(即使它碰巧在 main 中)。

          要在 Person 中存儲(chǔ)非 'static 字符串,它需要:

          A)基于生命周期的泛型:

          struct?Person<'a>?{
          ????name:?&'a?str,
          }

          fn?main()?{
          ????let?name?=?format!("fasterthan{}",?"lime");
          ????let?p?=?Person?{?name:?&name?};
          ????//?`p`?cannot?outlive?`name`
          }

          B)取得字符串所有權(quán)

          struct?Person?{
          ????name:?String,
          }

          fn?main()?{
          ????let?name?=?format!("fasterthan{}",?"lime");
          ????let?p?=?Person?{?name:?name?};
          ????//?`name`?was?moved?into?`p`,?their?lifetimes?are?no?longer?tied.
          }

          在 struct literal 中,當(dāng)一個(gè)字段被設(shè)置為同名的變量綁定時(shí):

          let?p?=?Person?{?name:?name?};

          它可以這樣簡(jiǎn)寫:

          let?p?=?Person?{?name?};

          09 所有權(quán)

          對(duì)于 Rust 中的許多類型,存在有所有權(quán)和沒(méi)有所有權(quán)之分:

          • Strings:String 是有所有權(quán)的;$str 是一個(gè)引用;
          • Paths:PathBuf 是有所有權(quán)的;$Path 是一個(gè)引用;
          • Collections:Vec 是有所有權(quán)的;&[T] 是一個(gè)引用;

          Rust 有切片類型(slice)—— 它們是對(duì)多個(gè)連續(xù)元素的引用。

          你可以借用 Vector 的一個(gè)切片,例如:

          fn?main()?{
          ????let?v?=?vec![1,?2,?3,?4,?5];
          ????let?v2?=?&v[2..4];
          ????println!("v2?=?{:?}",?v2);
          }

          //?output:
          //?v2?=?[3,?4]

          以上并不神奇。索引操作符(foo[index])被 Index 和 IndexMut 特征重載。

          .. 語(yǔ)法是 range 字面值,range 是標(biāo)準(zhǔn)庫(kù)中定義的幾個(gè)結(jié)構(gòu)體。

          它們可以是半閉半開的,如果前面有 = ,那么它們就是閉區(qū)間。

          fn?main()?{
          ????//?0?or?greater
          ????println!("{:?}",?(0..).contains(&100));?//?true
          ????//?strictly?less?than?20
          ????println!("{:?}",?(..20).contains(&20));?//?false
          ????//?20?or?less?than?20
          ????println!("{:?}",?(..=20).contains(&20));?//?true
          ????//?only?3,?4,?5
          ????println!("{:?}",?(3..6).contains(&4));?//?true
          }

          借用規(guī)則適用于切片。

          fn?tail(s:?&[u8])?->?&[u8]?{
          ??&s[1..]?
          }

          fn?main()?{
          ????let?x?=?&[1,?2,?3,?4,?5];
          ????let?y?=?tail(x);
          ????println!("y?=?{:?}",?y);
          }

          這和下面的一樣:

          fn?tail<'a>(s:?&'a?[u8])?->?&'a?[u8]?{
          ??&s[1..]?
          }

          以下是合法的:

          fn?main()?{
          ????let?y?=?{
          ????????let?x?=?&[1,?2,?3,?4,?5];
          ????????tail(x)
          ????};
          ????println!("y?=?{:?}",?y);
          }

          但因?yàn)?[1,2,3,4,5] 是一個(gè) 'static 數(shù)組。所以,以下是非法的:

          fn?main()?{
          ????let?y?=?{
          ????????let?v?=?vec![1,?2,?3,?4,?5];
          ????????tail(&v)
          ????????//?error:?`v`?does?not?live?long?enough
          ????};
          ????println!("y?=?{:?}",?y);
          }

          因?yàn)?Vector 是在堆分配的,而且它的生命周期是非 'static 的。

          &str 實(shí)際上是切片。

          fn?file_ext(name:?&str)?->?Option<&str>?{
          ????//?this?does?not?create?a?new?string?-?it?returns
          ????//?a?slice?of?the?argument.
          ????name.split(".").last()
          }

          fn?main()?{
          ????let?name?=?"Read?me.?Or?don't.txt";
          ????if?let?Some(ext)?=?file_ext(name)?{
          ????????println!("file?extension:?{}",?ext);
          ????}?else?{
          ????????println!("no?file?extension");
          ????}
          }

          所以,借用規(guī)則也適用它:

          fn?main()?{
          ????let?ext?=?{
          ????????let?name?=?String::from("Read?me.?Or?don't.txt");
          ????????file_ext(&name).unwrap_or("")
          ????????//?error:?`name`?does?not?live?long?enough
          ????};
          ????println!("extension:?{:?}",?ext);
          }

          10 錯(cuò)誤處理

          可能失敗的函數(shù)通常返回一個(gè) Result:

          fn?main()?{
          ????let?s?=?std::str::from_utf8(&[240,?159,?141,?137]);
          ????println!("{:?}",?s);
          ????//?prints:?Ok("??")

          ????let?s?=?std::str::from_utf8(&[195,?40]);
          ????println!("{:?}",?s);
          ????//?prints:?Err(Utf8Error?{?valid_up_to:?0,?error_len:?Some(1)?})
          }

          如果你想在失敗的情況下 panic,你可以調(diào)用 unwrap():

          fn?main()?{
          ????let?s?=?std::str::from_utf8(&[240,?159,?141,?137]).unwrap();
          ????println!("{:?}",?s);
          ????//?prints:?"??"

          ????let?s?=?std::str::from_utf8(&[195,?40]).unwrap();
          ????//?prints:?thread?'main'?panicked?at?'called?`Result::unwrap()`
          ????//?on?an?`Err`?value:?Utf8Error?{?valid_up_to:?0,?error_len:?Some(1)?}',
          ????//?src/libcore/result.rs:1165:5
          }

          或者 .expect(),它可以提供自定義錯(cuò)誤信息。

          fn?main()?{
          ????let?s?=?std::str::from_utf8(&[195,?40]).expect("valid?utf-8");
          ????//?prints:?thread?'main'?panicked?at?'valid?utf-8:?Utf8Error
          ????//?{?valid_up_to:?0,?error_len:?Some(1)?}',?src/libcore/result.rs:1165:5
          }

          或者你也可以使用 match:

          fn?main()?{
          ????match?std::str::from_utf8(&[240,?159,?141,?137])?{
          ????????Ok(s)?=>?println!("{}",?s),
          ????????Err(e)?=>?panic!(e),
          ????}
          ????//?prints???
          }

          也可以使用 if let:

          fn?main()?{
          ????if?let?Ok(s)?=?std::str::from_utf8(&[240,?159,?141,?137])?{
          ????????println!("{}",?s);
          ????}
          ????//?prints???
          }

          或者你可以拋出這樣的錯(cuò)誤:

          fn?main()?->?Result<(),?std::str::Utf8Error>?{
          ????match?std::str::from_utf8(&[240,?159,?141,?137])?{
          ????????Ok(s)?=>?println!("{}",?s),
          ????????Err(e)?=>?return?Err(e),
          ????}
          ????Ok(())
          }

          或者你可以使用 ? 來(lái)簡(jiǎn)潔地完成它:

          fn?main()?->?Result<(),?std::str::Utf8Error>?{
          ????let?s?=?std::str::from_utf8(&[240,?159,?141,?137])?;
          ????println!("{}",?s);
          ????Ok(())
          }

          操作符 * 可用于解引用,但是訪問(wèn)字段或調(diào)用方法不需要解引用:

          struct?Point?{
          ????x:?f64,
          ????y:?f64,
          }

          fn?main()?{
          ????let?p?=?Point?{?x:?1.0,?y:?3.0?};
          ????let?p_ref?=?&p;
          ????println!("({},?{})",?p_ref.x,?p_ref.y);
          }

          //?prints?`(1,?3)`

          而且你只能在類型為 Copy 的情況下才能這么做:

          struct?Point?{
          ????x:?f64,
          ????y:?f64,
          }

          fn?negate(p:?Point)?->?Point?{
          ????Point?{
          ????????x:?-p.x,
          ????????y:?-p.y,
          ????}
          }

          fn?main()?{
          ????let?p?=?Point?{?x:?1.0,?y:?3.0?};
          ????let?p_ref?=?&p;
          ????negate(*p_ref);
          ????//?error:?cannot?move?out?of?`*p_ref`?which?is?behind?a?shared?reference
          }
          //?now?`Point`?is?`Copy`
          #[derive(Clone,?Copy)]
          struct?Point?{
          ????x:?f64,
          ????y:?f64,
          }

          fn?negate(p:?Point)?->?Point?{
          ????Point?{
          ????????x:?-p.x,
          ????????y:?-p.y,
          ????}
          }

          fn?main()?{
          ????let?p?=?Point?{?x:?1.0,?y:?3.0?};
          ????let?p_ref?=?&p;
          ????negate(*p_ref);?//?...and?now?this?works
          }

          11 閉包

          閉包是 Fn、 FnMut 或 FnOnce 類型的函數(shù),包含一些捕獲的上下文。

          它們的參數(shù)是一對(duì)管道(|)中以逗號(hào)分隔的名稱列表。它們不需要大括號(hào),除非您希望有多個(gè)語(yǔ)句。

          fn?for_each_planet(f:?F)
          ????where?F:?Fn(&'static?str)
          {
          ????f("Earth");
          ????f("Mars");
          ????f("Jupiter");
          }
          ?
          fn?main()?{
          ????for_each_planet(|planet|?println!("Hello,?{}",?planet));
          }

          //?prints:
          //?Hello,?Earth
          //?Hello,?Mars
          //?Hello,?Jupiter

          借款規(guī)則也適用于它們:

          fn?for_each_planet(f:?F)
          ????where?F:?Fn(&'static?str)
          {
          ????f("Earth");
          ????f("Mars");
          ????f("Jupiter");
          }
          ?
          fn?main()?{
          ????let?greeting?=?String::from("Good?to?see?you");
          ????for_each_planet(|planet|?println!("{},?{}",?greeting,?planet));
          ????//?our?closure?borrows?`greeting`,?so?it?cannot?outlive?it
          }

          例如,這樣做不會(huì)奏效:

          fn?for_each_planet(f:?F)
          ????where?F:?Fn(&'static?str)?+?'static?//?`F`?must?now?have?"'static"?lifetime
          {
          ????f("Earth");
          ????f("Mars");
          ????f("Jupiter");
          }

          fn?main()?{
          ????let?greeting?=?String::from("Good?to?see?you");
          ????for_each_planet(|planet|?println!("{},?{}",?greeting,?planet));
          ????//?error:?closure?may?outlive?the?current?function,?but?it?borrows
          ????//?`greeting`,?which?is?owned?by?the?current?function
          }

          但這樣可以:

          fn?main()?{
          ????let?greeting?=?String::from("You're?doing?great");
          ????for_each_planet(move?|planet|?println!("{},?{}",?greeting,?planet));
          ????//?`greeting`?is?no?longer?borrowed,?it?is?*moved*?into
          ????//?the?closure.
          }

          需要可變地借用 FnMut 才能調(diào)用它,因此一次只能調(diào)用它一次。

          這是合法的:

          fn?foobar(f:?F)
          ????where?F:?Fn(i32)?->?i32
          {
          ????println!("{}",?f(f(2)));?
          }
          ?
          fn?main()?{
          ????foobar(|x|?x?*?2);
          }

          //?output:?8

          但以下不行:

          fn?foobar(mut?f:?F)
          ????where?F:?FnMut(i32)?->?i32
          {
          ????println!("{}",?f(f(2)));?
          ????//?error:?cannot?borrow?`f`?as?mutable?more?than?once?at?a?time
          }
          ?
          fn?main()?{
          ????foobar(|x|?x?*?2);
          }

          不過(guò)下面又是合法的:

          fn?foobar(mut?f:?F)
          ????where?F:?FnMut(i32)?->?i32
          {
          ????let?tmp?=?f(2);
          ????println!("{}",?f(tmp));?
          }
          ?
          fn?main()?{
          ????foobar(|x|?x?*?2);
          }

          //?output:?8

          FnMut 的存在是因?yàn)橐恍╅]包可變地借用了局部變量:

          fn?foobar(mut?f:?F)
          ????where?F:?FnMut(i32)?->?i32
          {
          ????let?tmp?=?f(2);
          ????println!("{}",?f(tmp));?
          }
          ?
          fn?main()?{
          ????let?mut?acc?=?2;
          ????foobar(|x|?{
          ????????acc?+=?1;
          ????????x?*?acc
          ????});
          }

          //?output:?24

          這些閉包不能傳遞給期待 Fn 的函數(shù):

          fn?foobar(f:?F)
          ????where?F:?Fn(i32)?->?i32
          {
          ????println!("{}",?f(f(2)));?
          }
          ?
          fn?main()?{
          ????let?mut?acc?=?2;
          ????foobar(|x|?{
          ????????acc?+=?1;
          ????????//?error:?cannot?assign?to?`acc`,?as?it?is?a
          ????????//?captured?variable?in?a?`Fn`?closure.
          ????????//?the?compiler?suggests?"changing?foobar
          ????????//?to?accept?closures?that?implement?`FnMut`"
          ????????x?*?acc
          ????});
          }

          FnOnce 閉包只能調(diào)用一次。它們的存在是因?yàn)橐恍╅]包移出了捕獲時(shí)移動(dòng)的變量:

          fn?foobar(f:?F)
          ????where?F:?FnOnce()?->?String
          {
          ????println!("{}",?f());?
          }
          ?
          fn?main()?{
          ????let?s?=?String::from("alright");
          ????foobar(move?||?s);
          ????//?`s`?was?moved?into?our?closure,?and?our
          ????//?closures?moves?it?to?the?caller?by?returning
          ????//?it.?Remember?that?`String`?is?not?`Copy`.
          }

          這是自然地強(qiáng)制執(zhí)行的,因?yàn)?FnOnce 閉包需要移動(dòng)才能被調(diào)用。

          以下例子是無(wú)效的:

          fn?foobar(f:?F)
          ????where?F:?FnOnce()?->?String
          {
          ????println!("{}",?f());?
          ????println!("{}",?f());?
          ????//?error:?use?of?moved?value:?`f`
          }

          而且,如果你需要有說(shuō)服力的證明我們的關(guān)閉確實(shí)會(huì)發(fā)生變化,這也是非法的:

          fn?main()?{
          ????let?s?=?String::from("alright");
          ????foobar(move?||?s);
          ????foobar(move?||?s);
          ????//?use?of?moved?value:?`s`
          }

          不過(guò)這樣是正常的:

          fn?main()?{
          ????let?s?=?String::from("alright");
          ????foobar(||?s.clone());
          ????foobar(||?s.clone());
          }

          下面是一個(gè)包含兩個(gè)參數(shù)的閉包:

          fn?foobar(x:?i32,?y:?i32,?is_greater:?F)
          ????where?F:?Fn(i32,?i32)?->?bool
          {
          ????let?(greater,?smaller)?=?if?is_greater(x,?y)?{
          ????????(x,?y)
          ????}?else?{
          ????????(y,?x)
          ????};
          ????println!("{}?is?greater?than?{}",?greater,?smaller);
          }
          ?
          fn?main()?{
          ????foobar(32,?64,?|x,?y|?x?>?y);
          }

          以下是一個(gè)閉包,但忽略了它的兩個(gè)參數(shù):

          fn?main()?{
          ????foobar(32,?64,?|_,?_|?panic!("Comparing?is?futile!"));
          }

          下面是一個(gè)有點(diǎn)令人擔(dān)憂的閉包:

          fn?countdown(count:?usize,?tick:?F)
          ????where?F:?Fn(usize)
          {
          ????for?i?in?(1..=count).rev()?{
          ????????tick(i);
          ????}
          }
          ?
          fn?main()?{
          ????countdown(3,?|i|?println!("tick?{}...",?i));
          }

          //?output:
          //?tick?3...
          //?tick?2...
          //?tick?1...

          下面是一個(gè) toilet 閉包:

          fn?main()?{
          ????countdown(3,?|_|?());
          }

          之所以這么叫是因?yàn)?|_| () 看起來(lái)像個(gè)廁所(toilet )。

          任何可迭代的東西都可以在 for in 循環(huán)中使用。

          我們剛剛看到一個(gè) range 被使用,但是它也適用于一個(gè) Vec:

          fn?main()?{
          ????for?i?in?vec![52,?49,?21]?{
          ????????println!("I?like?the?number?{}",?i);
          ????}
          }

          或者切片:

          fn?main()?{
          ????for?i?in?&[52,?49,?21]?{
          ????????println!("I?like?the?number?{}",?i);
          ????}
          }

          //?output:
          //?I?like?the?number?52
          //?I?like?the?number?49
          //?I?like?the?number?21

          或者是迭代器:

          fn?main()?{
          ????//?note:?`&str`?also?has?a?`.bytes()`?iterator.
          ????//?Rust's?`char`?type?is?a?"Unicode?scalar?value"
          ????for?c?in?"rust".chars()?{
          ????????println!("Give?me?a?{}",?c);
          ????}
          }

          //?output:
          //?Give?me?a?r
          //?Give?me?a?u
          //?Give?me?a?s
          //?Give?me?a?t

          即使迭代的項(xiàng)目被 filter、map 或 flat:

          fn?main()?{
          ????for?c?in?"SuRPRISE?INbOUND"
          ????????.chars()
          ????????.filter(|c|?c.is_lowercase())
          ????????.flat_map(|c|?c.to_uppercase())
          ????{
          ????????print!("{}",?c);
          ????}
          ????println!();
          }

          //?output:?UB

          你可以從函數(shù)返回一個(gè)閉包:

          fn?make_tester(answer:?String)?->?impl?Fn(&str)?->?bool?{
          ????move?|challenge|?{
          ????????challenge?==?answer
          ????}
          }

          fn?main()?{
          ????//?you?can?use?`.into()`?to?perform?conversions
          ????//?between?various?types,?here?`&'static?str`?and?`String`
          ????let?test?=?make_tester("hunter2".into());
          ????println!("{}",?test("******"));
          ????println!("{}",?test("hunter2"));
          }

          你甚至可以將對(duì)某個(gè)函數(shù)參數(shù)的引用移動(dòng)到它返回的閉包中:

          fn?make_tester<'a>(answer:?&'a?str)?->?impl?Fn(&str)?->?bool?+?'a?{
          ????move?|challenge|?{
          ????????challenge?==?answer
          ????}
          }

          fn?main()?{
          ????let?test?=?make_tester("hunter2");
          ????println!("{}",?test("*******"));
          ????println!("{}",?test("hunter2"));
          }

          //?output:
          //?false
          //?true

          或者,用省略的生命周期:

          fn?make_tester(answer:?&str)?->?impl?Fn(&str)?->?bool?+?'_?{
          ????move?|challenge|?{
          ????????challenge?==?answer
          ????}
          }

          總結(jié)

          以上就是這個(gè) 30 分鐘教程的全部?jī)?nèi)容。作者認(rèn)為,通過(guò)以上的學(xué)習(xí),你應(yīng)該能夠閱讀網(wǎng)上找到的大部分 Rust 代碼了。

          讀 Rust 代碼和寫 Rust 代碼很不一樣。一方面,你不是讀一個(gè)問(wèn)題的解決方案,而是實(shí)際解決它;另一方面,Rust 編譯器很強(qiáng)大,可以給你很多幫助。

          對(duì)于這篇教程中故意的錯(cuò)誤代碼,Rust 編譯器總是能非常好的提示你,同時(shí)給你很好的建議。

          原文鏈接:https://fasterthanli.me/articles/a-half-hour-to-learn-rust

          Rust 編程指北編譯,非直接翻譯。

          實(shí)話說(shuō),這個(gè)教程不可能 30 分鐘學(xué)完,即使學(xué)完了,肯定也沒(méi)入門。不過(guò)通過(guò)這些代碼,自己動(dòng)手實(shí)踐,對(duì)學(xué)習(xí) Rust 還是有幫助的!



          推薦閱讀


          福利

          我為大家整理了一份從入門到進(jìn)階的Go學(xué)習(xí)資料禮包,包含學(xué)習(xí)建議:入門看什么,進(jìn)階看什么。關(guān)注公眾號(hào) 「polarisxu」,回復(fù)?ebook?獲??;還可以回復(fù)「進(jìn)群」,和數(shù)萬(wàn) Gopher 交流學(xué)習(xí)。

          瀏覽 84
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  无码高清 | 97俺去| 亚洲电影无码 | 日皮在线观看 | 国产又黄又爽又粗又大免费视频 |