<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>

          Rust 勸退系列 05:復合數(shù)據(jù)類型

          共 5771字,需瀏覽 12分鐘

           ·

          2021-05-09 05:40

          閱讀本文大概需要 8 分鐘。

          大家好,我是站長 polarisxu。

          這是 Rust 勸退系列的第 5 個教程,探討 Rust 中的復合數(shù)據(jù)類型(Compound types)。Rust 中有兩種原生的復合類型:元組(tuple)和數(shù)組(array),順帶介紹切片。

          01 元組類型

          Go 語言沒有元組類型,但多返回值有點類似元組(但還是有區(qū)別的哦)。Python 中有元組類型,因此如果你熟悉 Python,對元組應該很熟悉。

          什么是元組類型?

          元組是一個可以包含各種類型的值的組合。元組是一個將多個其他類型的值組合進一個復合類型的主要方式。元組長度固定:一旦聲明,其長度無法增大或縮小。元組的類型由各組成元素類型的序列定義。

          元組通過小括號定義,里面的元素通過逗號分隔,例如:

          (23.227'a');

          這個字面值元組的類型是:(f64, i32, char),即對應每個元素的默認類型。因此,我們可以通過 let 將這個元組綁定到變量上,Rust 會進行類型推斷:

          let tup = (23.227'a');

          在 VSCode 中可以看到 tup 的類型就是:(f64, i32, char)。同樣地,我們也可以為 tup 使用類型注解:

          let tup: (f32i8char) = (23.227'a');

          因為元組是多個類型的集合,對元組中的類型沒有限制。因此,可以嵌套。比如:

          (2, (2.1'a'), false);

          不過建議別嵌套太多,否則可讀性太差。

          如何訪問元組元素呢?

          上面說,Go 語言中函數(shù)多返回值類似元組,在接收多返回值時,通過多個變量接收,比如:

          // Go 語言
          f, err := os.Open("abc.txt")

          在 Rust 中,可以解構元組(這也叫模式匹配解構):

          let tup = (23.227'a');
          let (x, y, z) = tup; // 注意:需要小括號

          和 Go 語言一樣,如果某個元素我們不關心,可以放入垃圾桶(_):

          let tup = (23.227'a');
          let (x, _, z) = tup; // 注意:需要小括號

          Rust 中變量定義未使用,不會像 Go 一樣報錯,但會警告!

          除了模式匹配解構,還可以使用類似訪問數(shù)組元素的方式訪問元組元素,只不過不是用[],而是用 . 加索引的方式(索引也是從 0 開始):

          let tup = (23.227'a');
          println!("{}", tup.1); // 輸出:27

          特殊的元組

          當元組中只有一個元素時(即元組長度是 1),唯一的元素后面必須加上逗號:

          let tup = (2,); // 逗號不能少,否則會提示你,單個值應該去掉小括號。這是避免將小括號當做計算的優(yōu)先級

          自然,模式匹配解構元組時,也必須有逗號。

          如果元組沒有元素呢?即空元組。看下面的代碼:

          fn main() {
              let result = test_tuple();
              println!("{:?}", result);
          }

          fn test_tuple() {
              println!("test empty tuple");
          }

          你猜打印 result 是啥?

          擦,竟然是 (),即空元組。而且 Rust 給它專門取了一個名字:單元類型(unit type),也就是說,() 叫單元類型,它有一個唯一值:空元組 ()。而且,因為沒有任何元素,Rust 將其歸為變量類型。

          還嫌 Rust 不夠復雜嗎?就叫空元組不行嗎?非得搞一個單元類型,這么奇怪的類型。。。

          為了避免復雜性,我覺得大家將其理解為空元組即可。至于為什么這里會返回空元組,在函數(shù)部分會講解。

          注意:() 是不占空間的,這和 Go 中的空結構體類似。

          02 數(shù)組

          Rust 中的數(shù)組和 Go 中的類似,是不可變的,由元素類型和長度確定,且長度必須是編譯期常量。Rust 中,數(shù)組類型標記為 [T; size]。數(shù)組字面值使用 [] 表示:

          let a = [1234];

          同樣會進行類型推斷(包括長度)(這里推斷出 a 的類型是 [i32; 4]),也可以顯示進行類型注解:

          let a: [i84] = [1234];

          相比較而言,Rust 創(chuàng)建數(shù)組比 Go 簡單,它和 PHP 這樣的動態(tài)語言類似。在 Go 中一般這樣創(chuàng)建數(shù)組:

          // Go 語言
          a := [...]int{1234}

          也就是說,Go 中創(chuàng)建數(shù)組是,類型信息不能少,沒法跟 Rust 一樣進行類型推斷。

          除了上面的初始化方法,Rust 中還可以這樣簡單的初始化:

          let a = [-14]; // 4 個元素都是 -1

          Rust 變量必須初始化后才能使用,而 Go 語言中,變量會有默認值。所以,Go 中可以簡單的定義一個數(shù)組,然后使用默認的初始值。如:

          // Go 語言
          var a [4]int  // a 的值是:[0 0 0 0]

          此外,Rust 中數(shù)組總是分配在棧中的,因此可以認為數(shù)組是「值類型」,和 Go 一樣,我們不應該直接傳遞數(shù)組,而應該和 Go 一樣,使用 slice。

          03 切片(slice)

          Rust 中的切片和 Go 中的切片意思一樣,表示對數(shù)組部分元素的引用。但和 Go 不同的是,Rust 的切片沒有容量的概念,只有一個指向數(shù)據(jù)的指針和切片的長度。Rust 中切片的類型標記為 &[T],即對數(shù)組進行引用(&)就是切片。

          Go 語言中有直接創(chuàng)建切片的語法(比如 make),但 Rust 中沒有,它必須依賴數(shù)組或 Vec(以后講解),通過引用來創(chuàng)建。

          let xs = [12345];
          let slice = &xs;

          既然切片是數(shù)組元素的片段引用,那如何引用部分片段呢?

          在 Go 中是這么做的:

          var arr = [...]int{1234}
          var slice1 = arr[:]   // 結果是 [1 2 3 4],全部元素
          var slice2 = arr[1:3]  // 結果是 [2 3]
          var slice3 = arr[:3]  // 結果是 [1 2 3]
          var slice4 = arr[1:]  // 結果是 [2 3 4]

          而在 Rust 中是這么做的:(結果和上面一樣)

          let arr = [1234];
          let slice1 = &arr[..];
          let slice2 = &arr[1..3];
          let slice3 = &arr[..3];
          let slice4 = &arr[1..];

          看到不同了嗎?

          • Rust 中生成切片,需要引用(&);
          • Go 中使用 : 來引用片段;而 Rust 使用 ..

          相同的點是,都可以省略起始或終止位置,或都省略。

          關于 .. 以后還會講到

          切片類型的方法(也適用于數(shù)組)

          在 Rust 中,一切類型都有實現(xiàn)一些 trait,包括上一節(jié)的標量類型(用面向對象來講,一切皆對象)。現(xiàn)在先不探討 trait,著重看看 len 方法。具體參考標準庫文檔:https://doc.rust-lang.org/std/primitive.slice.html。

          1)len:計算長度

          數(shù)組或切片有一個 len() 方法可以計算長度。

          pub const fn len(&self) -> usize

          // 具體使用
          let arr = [123];
          assert_eq!(arr.len(), 3);  // assert_eq 和 println 一樣,是一個宏,用來斷言

          而 Go 語言中,使用 len(arr) 的形式,len 是內置函數(shù)。

          不過,關于 len 還有一些細小的點。看下面的 Go 代碼,你覺得有問題嗎?

          var arr = [...]int{1234}
          var slice = arr[:]

          var arr2 [len(arr)]int
          var arr3 [len(slice)]int

          在 Go  中,要求數(shù)組長度要求是編譯期常量。len(arr) 是編譯期常量,而 len(slice) 卻不是,因為 slice 的長度是可變的。所以,以上代碼 arr2 正確,arr3 編譯錯誤。

          那 Rust 中是怎么樣的呢?

          let arr = [1234];
          let slice = &arr[..];

          let arr2 = [0;arr.len()];
          let arr3 = [0;slice.len()];

          arr2 和 arr3 都編譯錯誤。arr3 錯誤可以理解,為什么 arr2 也不行呢?

          根據(jù)編譯器提示,怎么修改 arr2 就可以了:

          const ARR:[i324] = [1234];
          let arr2 = [0; ARR.len()];

          也就是說必須是數(shù)組常量。。。但數(shù)組本身不就是不可變的嗎?非得定義成常量,多此一舉?據(jù)說,Rust 有可能將數(shù)組改成可變的。。。有了切片,為啥還要把數(shù)組搞這么復雜?!

          2)其他方法

          • is_empty:判斷數(shù)組或切片是否為空
          • first:獲取第一個元素
          • last:獲取最后一個元素
          • 。。。

          first 和 last 有什么用?為啥不直接通過下標獲取?

          • last 的存在,使得我們不需要先調用 len 獲取長度來間接獲取最后一個元素。
          • 而 first 的存在,使得我們不需要先判斷是否為空。

          不過,因為存在數(shù)組或切片為空的情況,因此 first 和 last 返回的都是 Opiton 類型。關于該類型后續(xù)再講。

          04 小結

          我們用兩篇講解了 Rust 中的數(shù)據(jù)類型,同時和 Go 的數(shù)據(jù)類型進行了對比。但 Rust 中的數(shù)據(jù)類型不止這些,還有其他類型,我們以后再講,包括通過標準庫定義的數(shù)據(jù)類型。

          再強調一次,本系列教程的目標是讓大家學習盡可能不被勸退,因此有些特別復雜但我認為可以不用的,就不會介紹。關于 Rust 中的 primitive type 可以在標準庫文檔找到,以及每個類型的方法。https://doc.rust-lang.org/std/index.html#primitives。




          往期推薦


          我是 polarisxu,北大碩士畢業(yè),曾在 360 等知名互聯(lián)網(wǎng)公司工作,10多年技術研發(fā)與架構經(jīng)驗!2012 年接觸 Go 語言并創(chuàng)建了 Go 語言中文網(wǎng)!著有《Go語言編程之旅》、開源圖書《Go語言標準庫》等。


          堅持輸出技術(包括 Go、Rust 等技術)、職場心得和創(chuàng)業(yè)感悟!歡迎關注「polarisxu」一起成長!也歡迎加我微信好友交流:gopherstudio

          瀏覽 120
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  蜜臀久久99精品久久久久久宅男 | 欧美精品久久久久久久久爆乳 | 影音先锋成人av在线 | 青娱乐偷拍视频99 | 亚洲欧美成人在线视频 |