官术网_书友最值得收藏!

How to do it...

Just a few steps need to be followed in order to learn more about pattern matching:

  1. Create a new binary project using cargo new pattern-matching. This time, we'll run an actual executable! Again, open the project using Visual Studio Code or another editor. 
  2. Let's check out literal matching. Just like a switch-case statement in other languages, each matching arm can match to literals as well:
fn literal_match(choice: usize) -> String {
match choice {
0 | 1 => "zero or one".to_owned(),
2 ... 9 => "two to nine".to_owned(),
10 => "ten".to_owned(),
_ => "anything else".to_owned()
}
}
  1. However, pattern matching is way more powerful than that. For example, tuple elements can be extracted and selectively matched:
fn tuple_match(choices: (i32, i32, i32, i32)) -> String {
match choices {
(_, second, _, fourth) => format!("Numbers at positions 1
and 3 are {} and {} respectively", second, fourth)
}
}
  1. Destructuring (moving properties out of a struct into their own variables) is a powerful feature in conjunction with structs and enums. First, this facilitates the assigning of multiple variables in a single matching arm to values that are assigned to properties at the incoming struct instance. Now, let's define a few structs and enums:
enum Background {
Color(u8, u8, u8),
Image(&'static str),
}

enum UserType {
Casual,
Power
}

struct MyApp {
theme: Background,
user_type: UserType,
secret_user_id: usize
}

Then, the individual properties can be matched in a destructuring match. Enums work just as well—however, be sure to cover all possible variations; the compiler will notice (or use the special _ to match all). Matching is also done from top to bottom, so whichever rule applies first will be executed. The following snippet matches variations of the structs we just defined. It matches and assigns variables if a particular user type and theme is detected:

fn destructuring_match(app: MyApp) -> String {
match app {
MyApp { user_type: UserType::Power,
secret_user_id: uid,
theme: Background::Color(b1, b2, b3) } =>
format!("A power user with id >{}< and color background
(#{:02x}{:02x}{:02x})", uid, b1, b2, b3),
MyApp { user_type: UserType::Power,
secret_user_id: uid,
theme: Background::Image(path) } =>
format!("A power user with id >{}< and image background
(path: {})", uid, path),
MyApp { user_type: _, secret_user_id: uid, .. } => format!
("A regular user with id >{}<, individual backgrounds not
supported", uid),
}
}
  1. On top of the powerful regular matching, a guard can also enforce certain conditions. Similar to destructuring, we can add more constraints:
fn guarded_match(app: MyApp) -> String { 
match app {
MyApp { secret_user_id: uid, .. } if uid <= 100 => "You are
an early bird!".to_owned(),
MyApp { .. } => "Thank you for also joining".to_owned()
}
}
  1. So far, borrowing and ownership has not been a significant concern. However, the match clauses so far have all taken ownership and transferred it to the scope of the matching arm (anything after =>), which, unless you return it, means that the outside scope cannot do any other work with it. To remedy that, references can be matched as well:
fn reference_match(m: &Option<&str>) -> String {
match m {
Some(ref s) => s.to_string(),
_ => "Nothing".to_string()
}
}
  1. In order to go full circle, we have not yet matched a particular type of literal: the string literal. Due to their heap allocation, they are fundamentally different from types such as i32 or usize. Syntactically, however, they don't look different from any other form of matching:
  1. Now, let's tie it all together and build a main function that calls the various functions with the right parameters. Let's begin by printing a few simpler matches:
pub fn main() {
let opt = Some(42);
match opt {
Some(nr) => println!("Got {}", nr),
_ => println!("Found None")
}
println!();
println!("Literal match for 0: {}", literal_match(0));
println!("Literal match for 10: {}", literal_match(10));
println!("Literal match for 100: {}", literal_match(100));

println!();
println!("Literal match for 0: {}", tuple_match((0, 10, 0,
100)));

println!();
let mystr = Some("Hello");
println!("Matching on a reference: {}",
reference_match(&mystr));
println!("It's still owned here: {:?}", mystr);

Next, we can also print the destructured matches:

    println!();
let power = MyApp {
secret_user_id: 99,
theme: Background::Color(255, 255, 0),
user_type: UserType::Power
};
println!("Destructuring a power user: {}",
destructuring_match(power));

let casual = MyApp {
secret_user_id: 10,
theme: Background::Image("my/fav/image.png"),
user_type: UserType::Casual
};
println!("Destructuring a casual user: {}",
destructuring_match(casual));

let power2 = MyApp {
secret_user_id: 150,
theme: Background::Image("a/great/landscape.png"),
user_type: UserType::Power
};
println!("Destructuring another power user: {}",
destructuring_match(power2));

And lastly, let's see about guards and literal string matches on UTF symbols:

  1. The last step again involves running the program. Since this is not a library project, the results will be printed on the command line. Feel free to change any of the variables in the main function to see how it affects the output. Here's what the output should be:

Now, let's take a peek behind the scenes to understand the code better.

主站蜘蛛池模板: 太保市| 且末县| 台南县| 云和县| 扬中市| 翁牛特旗| 土默特右旗| 资兴市| 甘孜县| 焉耆| 绍兴县| 高州市| 镇江市| 江华| 牟定县| 五大连池市| 安顺市| 英山县| 北辰区| 岳阳市| 冀州市| 蒙城县| 岳池县| 治多县| 杨浦区| 广饶县| 什邡市| 棋牌| 太仆寺旗| 利川市| 咸丰县| 涿州市| 开封市| 晋江市| 建昌县| 京山县| 阳新县| 鄢陵县| 桦川县| 深州市| 浪卡子县|