Rust does not have a switch keyword but the match keyword that works like a switch examples in other languages like Java and C++. Generally, the match can run codes or return a single value. It cannot do both at the same time. Let us go through some examples that use the Rust match construct.
Options That Only Run Codes with Match (aka Switch)
We start with a simple Rust match statement example. Consider the following codes. We have two functions that only display texts on the console. Using the match construct, we run these functions depending on a numeric value. Okay, we have the user_choice variable that dictates how the match statement works.
1 2 3 4 5 6 7 8 9 10 11 12 | fn f1() { println!("In f1"); } fn f2() { println!("In f2"); } fn f_unknown() { println!("In unknown function"); } ... |
The primary function looks as follows.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | fn main() { let user_choice = 10; // Execute function based on user_choice's value match user_choice { 1 => f1(), 2 => f2(), _ => f_unknown(), }; |
Line 3 stores the numeric value. We use the user_choice variable with the match keyword to pass a value of 10 as an argument to the match construct. Lines 8 – 12 read as follows:
- If user_choice is equal to 1, run the f1 function. Otherwise, go to the following expression.
- If user_choice is equal to 2, invoke the f2 function. Otherwise, proceed to the following expression and so on.
- If user_choice is neither 1 nor 2, call the f_unknown function. The last expression in our match statement represents a catch-all condition.
When we run the codes, the function f_unknown() will because there is no matching for the numeric value of 10. For the catch-all condition, we use _ to mean any value not matching previous expressions. The codes will generate the following output.
1 | In unknown function |
Options That Only Return Values from Match (aka Switch)
Instead of calling functions or running blocks of codes, the match can return values with a bit of change in the match contract’s syntax. Notice line 4 in the codes – we use the let keyword together with the match keyword. Cool, no?
1 2 3 4 5 6 7 8 9 10 11 12 13 | fn main() { let user_choice = 2; let j = match user_choice { 1 => 100, 2 => 200, _ => 300, }; println!("{}", j); } |
When we run the codes, we will get the following output.
1 | 200 |
Mixing Options That Run Codes With Returning Values
Mixing match options that run codes with those that return values will not work. Let us explore it. Consider the following codes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | fn my_function(user_choice: i32) { println!("The value is {}", user_choice); } fn main() { let user_choice = 1; match user_choice { 1 => "The value is one", 2 => my_function(user_choice) } } |
We will get the following errors if we run these codes in this Rust match statement example.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | error[E0308]: `match` arms have incompatible types --> src\main.rs:12:14 | 10 | / match user_choice { 11 | | 1 => "The value is one", | | ------------------ this is found to be of type `&str` 12 | | 2 => my_function(user_choice) | | ^^^^^^^^^^^^^^^^^^^^^^^^ expected `&str`, found `()` 13 | | } | |_____- `match` arms have incompatible types error: aborting due to previous error For more information about this error, try `rustc --explain E0308`. error: could not compile `rust-key-hashmap`. To learn more, run the command again with --verbose. Process finished with exit code 101 |
There’s no switch in Rust. Okay, let us move on.
Using String and &Str with Match Example
If we use string (in lowercase) values in the match statement, we need to use static (or literal) string values. As a result, the argument we pass to the match statement must be of the same type. Consider the following codes.
1 2 3 4 5 6 7 8 9 10 | fn main() { let user_choice_tmp: String = "B".to_string(); let user_choice = &user_choice_tmp[..]; let j = match user_choice { "A" => 100, "B" => 200, _ => 300, }; println!("{}", j); } |
We start with a String type value from a literal string “B.”
1 | let user_choice_tmp: String = "B".to_string(); |
Then, we convert that value back to a literal string “B” on line 3 to match what string type the match statement expects (at line 4). Cool? Are you still with me? If yes, let us proceed. The codes will generate the following result.
1 | 200 |
What if we have a String type value and want to match it against a set of &str values?
Consider the following Rust code example.
1 2 3 4 5 6 7 | let s = String::from("hello"); match s.as_str() { "hello" => println!("Got hello"), "world" => println!("Got world"), _ => println!("Got something else"), } |
In this example, we use the as_str method to convert the String value to a slice of &str. We can then match on the slice using the patterns “hello”, “world”, and _, which represent any other value.
You can also use the match keyword to perform pattern matching on string slices, which are references to substrings of String or &str values. For example:
1 2 3 4 5 6 7 | let s = "hello world"; match &s[0..5] { "hello" => println!("Got hello"), "world" => println!("Got world"), _ => println!("Got something else"), } |
In this example, we are using the &s[0..5] expression to create a slice of the first 5 characters of the &str value s, and then matching on that slice.
Switching Using Rust enum With Match Example
Now, for the most exciting part – we can use enums with match statements. Consider the following codes. We have an enum Direction with NORTH, EAST, WEST, and SOUTH.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | enum Direction { NORTH, EAST, WEST, SOUTH } fn main() { let user_choice = Direction::EAST; let j = match user_choice { Direction::NORTH => 100, Direction::SOUTH => 200, Direction::EAST => 300, Direction::WEST => 400 }; println!("{}", j); } |
When we run the codes, we get the following output.
1 | 300 |
There is one crucial thing we need to keep in mind when using match statements with enum types – we need to list out all the options! Yes! Notice the match statement codes for this section; we list all directions. What happens if we forget to include one? Consider the following codes. On line 8, we excluded the Direction:: WEST.
1 2 3 4 5 6 7 8 9 10 11 | fn main() { let user_choice = Direction::EAST; let j = match user_choice { Direction::NORTH => 100, Direction::SOUTH => 200, Direction::EAST => 300, // Direction::WEST => 400 }; println!("{}", j); } |
We get the following error when we run the modified codes.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | error[E0004]: non-exhaustive patterns: `WEST` not covered --> src\main.rs:11:19 | 1 | / enum Direction { 2 | | NORTH, 3 | | EAST, 4 | | WEST, | | ---- not covered 5 | | SOUTH 6 | | } | |_- `Direction` defined here ... 11 | let j = match user_choice { | ^^^^^^^^^^^ pattern `WEST` not covered | = help: ensure that all possible cases are being handled, possibly by adding wildcards or more match arms = note: the matched value is of type `Direction` error: aborting due to previous error For more information about this error, try `rustc --explain E0004`. error: could not compile `rust-key-hashmap`. To learn more, run the command again with --verbose. Process finished with exit code 101 |
Those are some match statement examples in Rust.
Further Readings Rust Match (aka Switch)
Here are some links for other readings.
This post is now part of the Rust Programming Language For Beginners Tutorial.