1. 程式人生 > 其它 >Rust能力養成之(4):使用者的自由度

Rust能力養成之(4):使用者的自由度

圖片

前言

上一篇,介紹了Rust語言的

  • 條件與決策

  • Match表達

  • 迴圈語句

這一篇,介紹Rust語言的使用者自定義資料型別

  • 結構體

  • 列舉

顧名思義,使用者自定義型別是由使用者自己參與定義,這些資料型別或者是對基本資料型別的封裝(wrapper),或者是對既有使用者自定義型別的進一步結合。通常有3種形式,學過C的同學想必會有印象,也就是所謂的結構體(structures->structs),列舉(enumerations->enums)聯合(unions->union)

不可否認,這些資料型別都提升了資料的表現力。在Rust中,所提及的前兩種使用者自定義型別,要比C中的結構體和列舉在功能上更為強大;而在第三項---聯合,與C近似,具體會在後續章節中再談。如上所提及,本文主要介紹結構體和列舉。

結構體(Structs)

在Rust中,我們可以宣告三種形式的結構體。其中最簡單的是unit struct,於此可以姑且稱之為單元結構,語法上是由struct關鍵字,對應名稱和一個分號組成。以下程式碼示例定義了一個單元結構。

單元結構(unit struct)

//unit_struct.rs
structDummyStruct;fnmain(){letvalue=DummyStruct;}

非常簡單吧,該程式碼中定義了一個名為Dummy的單元結構。在main函式中,可以只使用其名稱來初始化該型別。變數value現在包含了Dummy的例項,並且在佔用系統資源方面是一個大小(size)為0的值。

在執行時,如果沒有與之相關的資料,單元結構不會索取任何空間。就其用途而言,主要表現為3種:

  • 用於對沒有資料或狀態關聯的實體進行建模。

  • 表示錯誤型別,因為結構本身就足以指代錯誤內容,已經不需要對其進行描述

  • 表徵狀態機(state machine)實現中的狀態

元組結構(tuple struct)

這裡再談一下第二種結構體,所謂的元組結構,其與資料相關聯。再者,不需要對單個欄位命名,可以通過在其定義中的位置來引用。比如你正在為你的圖形應用程式編寫一個顏色轉換/計算庫,並想在程式碼中表示RGB顏色的數值。

我們看下如下程式碼:​​​​​​​

//tuple_struct.rsstructColor(u8,u8,u8);
fnmain(){letwhite=Color(255,255,255);//Youcanpullthemoutbyindexletred=white.0;letgreen=white.1;letblue=white.2; println!("Redvalue:{}",red);println!("Greenvalue:{}",green);println!("Bluevalue:{}\n",blue); letorange=Color(255,165,0); //YoucanalsodestructurethefieldsdirectlyletColor(r,g,b)=orange;println!("R:{},G:{},B:{}(orange)",r,g,b); //CanalsoignorefieldswhiledestructuringletColor(r,_,b)=orange;println!("R:{},G:{},B:{}(orange)",r,g,b);letColor(_,_,_)=orange;println!("R:{},G:{},B:{}(orange)",r,g,b);}

可見,

  • 第2行,定義Color是這裡的元組結構體

  • 第5行,為white變數賦值

  • 第8-10行, 通過variable.

    方式,訪問結構體變數域內引用位置,為對應變數賦值
  • 第12-14行,列印相應值

  • 第16行,為orange變數賦值

  • 第19-20行,用解構方式訪問結構體中的個體資料,並列印結果

    • 這顯然是訪問個體資料的有一種方式

  • 第22-26,如果你懶得一個一個寫個體引數,可以用下劃線_來應付一下各自位置,相應的結果依舊可以被正確推斷並打印出來。

該程式碼結果如下:

圖片

常規結構體(Common Struct)

對屬性在四五個以內的資料進行建模時,元組結構體是一個理想的選擇;一旦超過這個資料範圍,就會影響到程式碼嗎的可讀性和語義推理。因此,對於具有三個以上欄位的資料型別,建議使用類似c的結構體,也就是要介紹的第三種形式,同時是最常用的一種。

我們看下以下程式碼:​​​​​​​

//structs.rs
structPlayer{name:String,iq:u8,friends:u8,score:u16}
fnbump_player_score(mutplayer:Player,score:u16){player.score+=120;println!("Updatedplayerstats:");println!("Name:{}",player.name);println!("IQ:{}",player.iq);println!("Friends:{}",player.friends);println!("Score:{}",player.score);}
fnmain(){letname="Alice".to_string();letplayer=Player{name,iq:171,friends:134,score:1129};
bump_player_score(player,120);}

這個程式碼讀下來,可見,

  • 第3-8行,該結構體定義方式與元組結構體相同。不同點在於是在大括號內進行較為正式的型別宣告和命名

  • 第10-17行,定義對結構體資料進行操作的函式,可以看到第一個引數是可變繫結,讀者可以想想用意何在。

  • 第20行,初始化變數name

  • 第21-24行,初始化結構體變數player,仔細看下大括號內部的結構,後三個是名稱+冒號+賦值,而name的寫法是一種省略的方式,被稱為field init shorthand

程式碼結果如下:

圖片

常規結構體,或者簡稱就是結構體,與元組結構體相比,一個優勢就在於可以任意順序對結構體物件進行賦值。此外,結構體所佔的空間大小一般是所有個體屬性之和,這其中會存在資料對齊的情況,除此之外,再無額外空間花銷。

列舉(enums)

當需要對不同種類物件進行建模時,可以使用列舉,在定義過程中,每一種可能性或屬性稱為變體(variants),這些變體可以定義為是否包含資料,而所包含的資料可以是任何基本型別、結構、元組結構,甚至是列舉。

然而,如果是遞迴,比如有一個列舉Foo,還有一個儲存Foo的變體,則該變體需要在一個指標型別之後,以避免遞迴無限型別定義。因為列舉也可以在棧上建立,所需要有一個預先確定的所佔空間,而無限的型別定義無法在編譯時確定該數值。

我們看一個例子。​​​​​​​

//enums.rs
#[derive(Debug)]enumDirection{N,E,S,W}
enumPlayerAction{Move{direction:Direction,speed:u8},Wait,Attack(Direction)}
fnmain(){letsimulated_player_action=PlayerAction::Move{direction:Direction::N,speed:2,};matchsimulated_player_action{PlayerAction::Wait=>println!("Playerwantstowait"),PlayerAction::Move{direction,speed}=>{println!("Playerwantstomoveindirection{:?}withspeed{}",direction,speed)}PlayerAction::Attack(direction)=>{println!("Playerwantstoattackdirection{:?}",direction)}};}

首先請讀者注意一下列舉定義的格式。再者,不難看出,這裡定義了兩個列舉,DirectionPlayerAction,基於此,可以建立例項,這些在主函式中已有體現,比如第21-23行

注意,列舉必須要初始化的,但是選擇任意一個變體就可以。根據給定的列舉值,進行匹配並執行相應結果,則是第25-34行的內容,不難看到其中的三種行為,Wait,Move和Attack。

最後,看下第3行的 #[derive(Debug)],該屬性為了保證println!()使用{:?}格式字串,是由Debug的特性生成方法來實現的。

程式碼結果如下:

圖片

結語

從函數語言程式設計的角度來看,結構體和列舉也被稱為代數資料型別(Algebraic Data Types ,ADTs),因為其可表示值的可能範圍可以使用代數規則表示。比如加法和乘法的列舉是各自變體運算值的範圍。後續還會對此再討論一下。

本篇介紹了兩種使用者自定義資料型別:結構體列舉,其中已經體現了Rust語言的一部分設計重點。下一篇,會講一下與型別相關的函式功能與方法

主要參考和建議讀者進一步閱讀的文獻

https://doc.rust-lang.org/book

1.Rust程式設計之道,2019, 張漢東

2.The Complete Rust Programming Reference Guide,2019, Rahul Sharma,Vesa Kaihlavirta,Claus Matzinger
3.Hands-On Data Structures and Algorithms with

4.Rust,2018,Claus Matzinger
5.Beginning Rust ,2018,Carlo Milanesi

6.Rust Cookbook,2017,Vigneshwer Dhinakaran