There is not much difference in control flow in Rust compared to other languages.
if#
The condition of if
does not include ()
, and the condition must be of type bool
.
if
is an expression, achieving the effect of the JavaScript ternary operator directly:
let x = if true { 1 } else { 2 }
Loops#
The loop control loop
is an infinite loop that I have never seen before. It can be terminated and skipped using break
and continue
. In Rust, break
can also return a value. The concepts of break
, continue
, and loop labels are also present in the for
loop in JavaScript, but I haven't encountered them much in ES6.
The standard library Range combined with for
feels interesting. When writing loops in JavaScript, I often wonder why there isn't a concise solution to control the number of iterations. Now this satisfies that.
match#
match
is similar to switch
in JavaScript, with strict checks to ensure that all possibilities are exhausted. It can be used with [[data types#enums]] to match and extract values contained in enum members.
let value = match some_value {
Some(i) => i,
None => 0
}
You can use the wildcard (thought other
other
was a keyword) or _
to handle other scenarios. The wildcard should be written as the last condition, otherwise the conditions written after it will be completely ineffective.
match some_num {
1 => call(),
3 => check(),
other => println!("{}", other),
// You can also use _ to not capture the specific value of the remaining cases
// _ => (),
}
if let#
For scenarios where only one case needs to be handled, the if let
syntax is more concise than match
, but it lacks the exhaustive check.
if let Some(value) = some_value {
println!("{}", value);
}
? Operator#
Similar to the optional chaining in JavaScript, it can be used to propagate errors or None values in Option
and Result
. It can only be written within the body of a function that is compatible with the return value. For example, to use ?
with Result
, the function must return Result
. One difference is that with Result
, it can be used anywhere in the function body, and doing so will cause an early termination and return Result::Err
.
Pattern Matching#
A pattern is the syntax used in Rust to match the structure of types. It is somewhat similar to JavaScript's destructuring assignment, but with a slightly different logic. However, it can be used in more scenarios, and most control flows can be achieved using pattern matching.
The let
statement can be used for pattern matching, where the pattern on the left side matches the expression on the right side. In the branches of match
, the pattern on the left side matches the expression on the right side of the match
keyword. In the if let
statement, the pattern on the left side matches the expression on the right side.
let (x, y) = (1, 2);
match some_value {
Some(4) | Some(5) => {},
Some(x) => {},
None => {}
}
if let 1..=3 = some_value {
} else if is_true{
} else {
}
// Destructuring a struct
let User { name: username, age } = user;
In this sense, if let
is somewhat similar to the syntax if (pattern matching result)
.
Refutability#
If an expression has the possibility of not matching a pattern, then the pattern is refutable, such as the branch conditions in match
. Otherwise, it is irrefutable. It can also be said that in cases where all possibilities are exhausted, the pattern is irrefutable. let
and for
only accept irrefutable patterns.
Ignoring Values in Patterns#
- Use
_
to ignore a single value, and it can be repeated within a pattern. - Use
..
to ignore the remaining values, but it must ensure that the ignored part is unambiguous.
Matching Guards#
After the branch condition in match
, an if
keyword can be used to add a guard, which is an additional matching condition.
match some_value {
Some(1) | Some(2) => {},
Some(v) if v > 0 => {},
Some(_) => {},
_ => {}
}
The if
added applies to the entire pattern before it. If multiple patterns are specified using the |
operator, the if
before it has higher priority. I will probably be caught by this in the future.
match x {
// This will not match no matter what x is
1 | 2 | 3 if false => {},
_ => {}
}
If you want to simultaneously capture a variable and match a pattern, you can use @
binding.
match user {
// The age here is used for matching, and the value of age cannot be accessed in the following block
User { age: 1..=18, .. } => {},
// This way, it can be used
User { age: user_age @ 20..=35, .. } => {}
}