3 使用者自訂類型
Rust自訂類型主要通過下面兩個關鍵進行定義: struct:定義一個結構 enum:定義一個枚舉 常量能以通過const和static關鍵字建立。
3.1 結構
有三種類型的結構(“structs”),可以使用struct關鍵字來建立: 元組結構體,又名元組 傳統C結構體 元結構體,無field,為了做成通用類型
// A unit structstruct Nil;// A tuple structstruct Pair(i32, f64);// A struct with two fieldsstruct Point { x: f64, y: f64,}// Structs can be reused as fields of another struct#[allow(dead_code)]struct Rectangle { p1: Point, p2: Point,}fn main() { // Instantiate a `Point` let point: Point = Point { x: 0.3, y: 0.4 }; // Access the fields of the point println!("point coordinates: ({}, {})", point.x, point.y); // Destructure the point using a `let` binding let Point { x: my_x, y: my_y } = point; let _rectangle = Rectangle { // struct instantiation is an expression too p1: Point { x: my_y, y: my_x }, p2: point, }; // Instantiate a unit struct let _nil = Nil; // Instantiate a tuple struct let pair = Pair(1, 0.1); // Destructure a tuple struct let Pair(integer, decimal) = pair; println!("pair contains {:?} and {:?}", integer, decimal);}
程式執行結果:
point coordinates: (0.3, 0.4)pair contains 1 and 0.1
3.2 枚舉
enum關鍵字允許建立一個可能有許多變體的變數。每一個對結構體來說是合法的變體,對枚舉同樣是合法的。
// An attribute to hide warnings for unused code.#![allow(dead_code)]// Create an `enum` to classify someone. Note how both names// and type information together specify the variant:// `Skinny != Fat` and `Height(i32) != Weight(i32)`. Each// is different and independent.enum Person { // An `enum` may either be `unit-like`, Skinny, Fat, // like tuple structs, Height(i32), Weight(i32), // or like structures. Info { name: String, height: i32 }}// A function which takes a `Person` enum as an argument and// returns nothing.fn inspect(p: Person) { // Usage of an `enum` must cover all cases (irrefutable) // so a `match` is used to branch over it. match p { Person::Skinny => println!("Is skinny!"), Person::Fat => println!("Is fat!"), // Destructure `i` from inside the `enum`. Person::Height(i) => println!("Has a height of {}.", i), Person::Weight(i) => println!("Has a weight of {}.", i), // Destructure `Info` into `name` and `height`. Person::Info { name, height } => { println!("{} is {} tall!", name, height); }, }}fn main() { let person = Person::Height(18); let danny = Person::Weight(10); // `to_owned()` creates an owned `String` from a string slice. let dave = Person::Info { name: "Dave".to_owned(), height: 72 }; let john = Person::Fat; let larry = Person::Skinny; inspect(person); inspect(danny); inspect(dave); inspect(john); inspect(larry);}
程式運行結果:
Has a height of 18.Has a weight of 10.Dave is 72 tall!Is fat!Is skinny!
3.2.1 use
可以使用use聲明,所以不用手動指定範圍:
// An attribute to hide warnings for unused code.#![allow(dead_code)]enum Status { Rich, Poor,}enum Work { Civilian, Soldier,}fn main() { // Explicitly `use` each name so they are available without // manual scoping. use Status::{Poor, Rich}; // Automatically `use` each name inside `Work`. use Work::*; // Equivalent to `Status::Poor`. let status = Poor; // Equivalent to `Work::Civilian`. let work = Civilian; match status { // Note the lack of scoping because of the explicit `use` above. Rich => println!("The rich have lots of money!"), Poor => println!("The poor have no money..."), } match work { // Note again the lack of scoping. Civilian => println!("Civilians work!"), Soldier => println!("Soldiers fight!"), }}
程式執行結果:
The poor have no money...Civilians work!
3.2.2 C-like
enum也能像C預言的枚舉那樣使用。
// An attribute to hide warnings for unused code.#![allow(dead_code)]// enum with implicit discriminator (starts at 0)enum Number { Zero, One, Two,}// enum with explicit discriminatorenum Color { Red = 0xff0000, Green = 0x00ff00, Blue = 0x0000ff,}fn main() { // `enums` can be cast as integers. println!("zero is {}", Number::Zero as i32); println!("one is {}", Number::One as i32); println!("roses are #{:06x}", Color::Red as i32); println!("violets are #{:06x}", Color::Blue as i32);}
程式運行結果為:
zero is 0one is 1roses are #ff0000violets are #0000ff
3.2.3 測試案例:鏈表
enums一個普通的使用時建立鏈表:
use List::*;enum List { // Cons: Tuple struct that wraps an element and a pointer to the next node Cons(u32, Box<List>), // Nil: A node that signifies the end of the linked list Nil,}// Methods can be attached to an enumimpl List { // Create an empty list fn new() -> List { // `Nil` has type `List` Nil } // Consume a list, and return the same list with a new element at its front fn prepend(self, elem: u32) -> List { // `Cons` also has type List Cons(elem, Box::new(self)) } // Return the length of the list fn len(&self) -> u32 { // `self` has to be matched, because the behavior of this method // depends on the variant of `self` // `self` has type `&List`, and `*self` has type `List`, matching on a // concrete type `T` is preferred over a match on a reference `&T` match *self { // Can't take ownership of the tail, because `self` is borrowed; // instead take a reference to the tail Cons(_, ref tail) => 1 + tail.len(), // Base Case: An empty list has zero length Nil => 0 } } // Return representation of the list as a (heap allocated) string fn stringify(&self) -> String { match *self { Cons(head, ref tail) => { // `format!` is similar to `print!`, but returns a heap // allocated string instead of printing to the console format!("{}, {}", head, tail.stringify()) }, Nil => { format!("Nil") }, } }}fn main() { // Create an empty linked list let mut list = List::new(); // Append some elements list = list.prepend(1); list = list.prepend(2); list = list.prepend(3); // Show the final state of the list println!("linked list has length: {}", list.len()); println!("{}", list.stringify());}
程式運行結果:
linked list has length: 33, 2, 1, Nil
3.3 常量
Rust有兩種不同類型的常量,這些常量可以在任何作用於定義,包括全域。兩種方法都需要顯示聲明: const:一個不可改變的值(通常用法) static:一個可能在staic聲明周期內可變的變數 “string”是一個特例。它可以直接賦值給一個static變數,而不需要修改,因為它的類型簽名:&'static str已經使用了'static生命週期。所有其他的參考型別必須使用'static聲明走起做特殊註解。
// Globals are declared outside all other scopes.static LANGUAGE: &'static str = "Rust";const THRESHOLD: i32 = 10;fn is_big(n: i32) -> bool { // Access constant in some function n > THRESHOLD}fn main() { let n = 16; // Access constant in the main thread println!("This is {}", LANGUAGE); println!("The threshold is {}", THRESHOLD); println!("{} is {}", n, if is_big(n) { "big" } else { "small" }); // Error! Cannot modify a `const`. //THRESHOLD = 5; // FIXME ^ Comment out this line}
程式運行結果:
This is RustThe threshold is 1016 is big