在上一章節的最後我們介紹到利用數組結構體來建立新的型別的方式,在這個章節我們會繼續介紹更多型別的應用。



型別的別名

我們在上一個章節,學習到可以利用「type」關鍵字來替特性定義出其關聯型別,而事實上「type」關鍵字並不一定只能在特性上。我們可以在任意的scope下,使用「type」關鍵字來定義出一個型別的新別名。舉例來說:

以上程式,我們替「i32」型別定義了一個新別名「Kilometers」,這個「Kilometers」並不算是一個新的型別,它很單純地就是代表著「i32」型別。定義新型別主要是用來代替某些需要使用泛型的型別,其在撰寫時又臭又長的語法。舉例來說:

以上程式,我們使用新別名「Thunk」來代替原本的「Box<Fn() + Send + 'static>」型別。

「Result<T, E>」列舉也是很常會定義新的別名來代替,因為通常在一個模組下,使用的Err變體所包裹的值都是同一個型別。例如標準函式庫中的「std::io」模組就會利用「type」關鍵字替「Result<T, E>」列舉定義出新的別名:

別名也支援泛型,而且也可以搭配「pub」關鍵字,使別名能夠被其它的模組存取。

空型別(Empty Type)

Rust程式語言還有內建一個十分特別的型別,那就是「!」,稱作「空型別」。有些函數或是方法,在它們被呼叫之後,就會進入無窮迴圈或是使程式直接結束,因此不會有將值回傳出去的機會。Rust程式語言的編譯器擁有型別推論的功能,在進行型別推論時,「空型別」的優先權會低於其它的型別,當同時遇到某個表達式可能會回傳空型別和其它型別時,便會忽略空型別,將這個表達式的回傳值型別推論為其它型別。

舉例來說:

以上程式碼中,我們定義了一個會回傳空型別的「panic」函數,這個函數在呼叫之後就會使程式panic,而沒有機會將值回傳出去。因此程式第8行到第12行的「if」和「else」關鍵字組成的表達式,其回傳值型別會被推論為「&str」。

我們再仔細觀察一下「panic」函數的實作方式,就會發現一些特別的地方。既然「panic」函數會回傳「空型別」的值的話,那麼它勢必得在主體中定義回傳的動作,即便這個動作可能不會有被執行的機會,也應該還是要有,否則程式應該不能編譯才對。難道,「panic!」巨集有回傳值,而且回傳值型別就是「空型別」?賓狗!沒錯,Rust程式語言有些內建的東西會回傳「空型別」,除了讓能程式panic的相關巨集之外,還有「loop」關鍵字所組成的表達式,在沒有「break」關鍵字的情況下也會回傳「空型別」。

可是怎麼還是有點奇怪?程式第2行,如果我們要讓「panic」函數回傳「panic!」巨集的回傳值的話,不是應該不能把它加上分號嗎?還有程式第12行,我們要讓「else」關鍵字組成的表達式能夠回傳「panic」函數的回傳值的話,不是也應該不能把它加上分號嗎?這是算是當程式敘述會回傳「空型別」時的例外用法,也就是可以無視它有沒有分號,只要放在程式區塊的最後一行,編譯器就會自動判斷它需不需要進行回傳,如果函數或是方法有定義要回傳「空型別」就會回傳;如果函數或是方法沒有定義回傳值型別,就不會回傳。

舉例來說:

以上程式第2行和第11行都必須要去掉分號才可以編譯成功。

再舉一個例子:

以上程式碼,雖然「panic!」巨集會回傳「空型別」,且「panic」函數沒有定義回傳值型別,但程式第2行,即便「panic!」巨集沒有加上分號也可以編譯成功。

動態大小的型別

在先前的章節中,我們學到了動態調度和靜態調度,因而了解到Rust程式語言要替一個變數或是參數來分配記憶體空間時,必須要在程式編譯時期就要知道要分配多少記憶體空間,否則就只能是用動態調度的方式,利用參考或是智慧型指標來存取資料。

在這邊我們來介紹一個相關的特性──「Sized」。只要是可以在編譯階段就知道其記憶體空間的型別,都會自動實作「Sized」特性。在使用泛型做靜態調度的時候,為了確保泛型型別參數實際所代表的型別是真的能夠在程式編譯階段就知道其記憶體空間,Rust程式語言其實會自動對泛型型別參數做限制,使它們實際所代表的型別至少要有實作「Sized」特性。

舉例來說:

其實就等於:

如果我們要讓泛型型別參數不要有實作「Sized」特性的限制,可以用「T: ?Sized」這樣的寫法來定義。當然,若編譯器無法保證「T」泛型型別參數所代表的型別是可以在編譯階段就知道大小的型別的話,就無法使用靜態調度了。

如以上這段程式,參數「t: T」會編譯錯誤。

結論

在這個章節我們學會了使用泛型的別名和空型別,也了解到泛型型別參數其實會自動有「型別必須實作『Sized』特性」的限制。至此我們已經將99%的Rust程式語言的觀念和規則都學完了,在下一章節,我們就來使用Rust程式語言實作出一個支援多執行緒的Web伺服器吧!

下一章:建立多執行緒的Web伺服器