Rust Enums
Enums in Rust are not as simple as the concept in other programming languages, but they can still be used very simply:
Example
enum Book {
Papery, Electronic
}
fn main() {
let book = Book::Papery;
println!("{:?}", book);
}
Output:
Papery
Books are divided into paper books (Papery book) and electronic books (Electronic book).
If you are now developing a library management system and need to describe the different attributes of the two types of books (paper books have a call number, e-books only have a URL), you can add tuple attribute descriptions to the enum members:
enum Book {
Papery(u32),
Electronic(String),
}
let book = Book::Papery(1001);
let ebook = Book::Electronic(String::from("url://..."));
If you want to name the attributes, you can use struct syntax:
enum Book {
Papery { index: u32 },
Electronic { url: String },
}
let book = Book::Papery{index: 1001};Although you can name them this way, note that you cannot access the attributes bound to an enum variant like you access struct fields. The way to access them is in the match syntax.
match Syntax
The purpose of enums is to classify a certain type of thing, and the purpose of classification is to describe different situations. Based on this principle, enum types are often ultimately handled by branching structures (switch in many languages). The switch syntax is classic, but it is not supported in Rust. The reason many languages abandon switch is that switch is prone to fall-through issues caused by forgetting to add break. Languages like Java and C# prevent this situation through safety checks.
Rust implements branching structures through the match statement. Let's first see how to use match to handle enums:
Example
enum Book {
Papery {index: u32},
Electronic {url: String},
}
let book = Book::Papery{index: 1001};
let ebook = Book::Electronic{url: String::from("url...")};
match book {
Book::Papery { index } => {
println!("Papery book {}", index);
},
Book::Electronic { url } => {
println!("E-book {}", url);
}
}
}
Output:
Papery book 1001
A match block can also be treated as a function expression, and it can have a return value:
match 枚举类实例 {
分类1 => 返回值表达式,
分类2 => 返回值表达式,
...
}But the types of all return value expressions must be the same!
If the additional attributes of an enum are defined as a tuple, you need to temporarily specify a name in the match block:
Example
Papery(u32),
Electronic {url: String},
}
let book = Book::Papery(1001);
match book {
Book::Papery(i) => {
println!("{}", i);
},
Book::Electronic { url } => {
println!("{}", url);
}
}
In addition to branching on enums, match can also branch on data of types such as integers, floating-point numbers, characters, and string slice references (&str). Among them, branching on floating-point numbers is legal, but it is not recommended because precision issues may cause branch errors.
When branching on non-enum types, you must handle the catch-all case, even if there is nothing to do in the catch-all case..The catch-all case uses an underscore_to represent:
Example
let t = "abc";
match t {
"abc" => println!("Yes"),
_ => {},
}
}
Option Enum
Option is an enum in the Rust standard library. This type is used to fill the gap that Rust does not support null references.
Many languages support the existence of null (C/C++, Java), which is convenient, but it also creates huge problems. The inventor of null also admitted this, "a convenient idea caused cumulative losses of one billion dollars."
null often deals a fatal blow to the program when developers treat everything as not null: after all, as long as one such error occurs, the program's execution must completely terminate.
To solve this problem, many languages do not allow null by default, but support the appearance of null at the language level (often modified with a ? symbol before the type).
Java supports null by default, but can restrict the appearance of null through the @NotNull annotation. This is a way to cope.
Rust completely disallows null values at the language level, but unfortunately null can efficiently solve a small number of problems, so Rust introduced the Option enum:
enum Option<T> {
Some(T),
None,
}If you want to define a type that can be null, you can do this:
let opt = Option::Some("Hello");If you want to perform some operations on opt, you must first determine whether it isOption::None:
Example
let opt = Option::Some("Hello");
match opt {
Option::Some(something) => {
println!("{}", something);
},
Option::None => {
println!("opt is nothing");
}
}
}
Output:
Hello
If your variable is initially null, consider the compiler: how does it know what type the variable is when the value is not null?
Therefore, an Option with an initial null value must have an explicit type:
Example
let opt: Option<&str> = Option::None;
match opt {
Option::Some(something) => {
println!("{}", something);
},
Option::None => {
println!("opt is nothing");
}
}
}
Output:
opt is nothing
This design makes programming with null values more difficult, but this is exactly what is needed to build a stable and efficient system. Since Option is introduced by default in the Rust compiler, you can omit Option:: and directly write None or Some() when using it.
Option is a special enum type that can contain values for branch selection:
Example
let t = Some(64);
match t {
Some(64) => println!("Yes"),
_ => println!("No"),
}
}
if let Syntax
Example
match i {
0 => println!("zero"),
_ => {},
}
Put it into the main function, the output is:
zero
The purpose of this program is to determine whether i is the number 0, and if so, print zero.
Now use if let syntax to shorten this code:
let i = 0;
if let 0 = i {
println!("zero");
}The syntax of if let is as follows:
if let 匹配值 = 源变量 {
语句块
}You can add an else block afterward to handle the catch-all case.
if let syntax can be considered "syntactic sugar" for a match statement that only distinguishes two cases (syntactic sugar refers to a convenient alternative with the same underlying principle as some syntax).
It is also applicable to enums:
Example
enum Book {
Papery(u32),
Electronic(String)
}
let book = Book::Electronic(String::from("url"));
if let Book::Papery(index) = book {
println!("Papery {}", index);
} else {
println!("Not papery book");
}
}