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

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.

主站蜘蛛池模板: 增城市| 东光县| 太仆寺旗| 叙永县| 柏乡县| 湾仔区| 平远县| 六盘水市| 石阡县| 阿图什市| 浦北县| 江达县| 团风县| 苗栗县| 枣强县| 旺苍县| 鲁甸县| 海原县| 龙里县| 滦南县| 西畴县| 呼图壁县| 洛浦县| 双桥区| 乌兰浩特市| 红桥区| 昌邑市| 清河县| 大安市| 和政县| 皮山县| 西吉县| 正阳县| 双城市| 常山县| 雷波县| 阿拉尔市| 铁力市| 崇州市| 友谊县| 新化县|