Introduction
A leap year (in the Gregorian calendar) occurs:
- In every year that is evenly divisible by 4.
- Unless the year is evenly divisible by 100, in which case it’s only a leap year if the year is also evenly divisible by 400.
Some examples:
- 1997 was not a leap year as it’s not divisible by 4.
- 1900 was not a leap year as it’s not divisible by 400.
- 2000 was a leap year!
For a delightful, four-minute explanation of the whole phenomenon of leap years, check out [this YouTube video](https://www.youtube.com/watch?v=xX96xng7sAE).
Instructions
Instructions
Your task is to determine whether a given year is a leap year.
Dig Deeper
Boolean one line
One line of Boolean conditions
pub fn is_leap_year(year: u64) -> bool {
(year % 4 == 0) && ((year % 100 != 0) || (year % 400 == 0))
}
The first boolean condition uses the remainder operator to check if the year is evenly divided by 4.
- If the year is not evenly divisible by
4, then the chain will “short circuit” due to the next operator being a logical AND (&&), and will return false.
- If the year is evenly divisible by
4, then the year is checked to not be evenly divisible by 100.
- If the year is not evenly divisible by
100, then the expression is true and the chain will “short-circuit” to return true,
since the next operator is a logical OR (||).
- If the year is evenly divisible by
100, then the expression is false, and the returned value from the chain will be if the year is evenly divisible by 400.
| year | year % 4 == 0 | year % 100 != 0 | year % 400 == 0 | is leap year |
|---|
| 2020 | true | true | not evaluated | true |
| 2019 | false | not evaluated | not evaluated | false |
| 2000 | true | false | true | true |
| 1900 | true | false | false | false |
The chain of boolean expressions is efficient, as it proceeds from testing the most likely to least likely conditions.
Ternary operator
Ternary expression of Boolean conditions
pub fn is_leap_year(year: u64) -> bool {
if year % 100 == 0 {
year % 400 == 0
} else {
year % 4 == 0
}
}
Unlike other languages, Rust does not have a ternary operator such as ? that is used like so
// this is NOT valid Rust code
a > b ? a : b
where a is tested to be greater than b, and , if so, returns a, otherwise it returns b.
Note that the line of code returns a or b without using a return keyword.
Because Rust is an expression-based language, a simple if/else works like a ternary expression in other languages.
In this code
if year % 100 == 0 {
year % 400 == 0
}
year is tested to be evenly divislbe by 100 by using the remainder operator.
If so, it returns if year is evenly divisible by 400, as the last expression evaluated in the function.
Note that the line is just `year % 400 == 0`.
This is because the [last expression can be returned](https://doc.rust-lang.org/rust-by-example/fn.html)
from a function without using `return` and a semicolon.
If year is not evenly divisible by 100, then else is the last expression evaluated in the function
else {
year % 4 == 0
}
and returns if year is evenly divisible by 4.
| year | year % 100 == 0 | year % 400 == 0 | year % 4 == 0 | is leap year |
|---|
| 2020 | false | not evaluated | true | true |
| 2019 | false | not evaluated | false | false |
| 2000 | true | true | not evaluated | true |
| 1900 | true | false | not evaluated | false |
Although it uses a maximum of only two checks, the ternary operator tests an outlier condition first,
making it less efficient than another approach that would first test if the year is evenly divisible by 4,
which is more likely than the year being evenly divisible by 100.
match on a tuple
match on a tuple
pub fn is_leap_year(year: u64) -> bool {
match (year % 4, year % 100, year % 400) {
(_, _, 0) => true,
(_, 0, _) => false,
(0, _, _) => true,
_ => false,
}
}
A tuple is made from the conditions for the year being evenly divisible by 4, 100 and 400.
- The
tuple is tested in a match expression.
- It tests from top to bottom, so the
(_, _, 0) => true, arm is tested first.
- It checks the values of the expressions in the
tuple, and uses the _ wildcard pattern to disregard a value inside the tuple.
- If the pattern matches, then the arm returns the value to the right of the
=>.
- If the pattern does not match, then the pattern in the next arm is tested.
- In the final arm of the
match, the wildcard pattern is applied to the entire tuple.
This is similar to default used in switch statements in other languages.
It returns false no matter what the values in the tuple are.
| year | year % 4 | year % 100 | year % 400 | is leap year |
|---|
| 2020 | 0 | 20 | 20 | true |
| 2019 | 3 | 19 | 19 | false |
| 2000 | 0 | 0 | 0 | true |
| 1900 | 0 | 0 | 300 | false |
Although some may consider it to be a more “functional” approach, the switch on a tuple approach is somewhat more verbose than other approaches,
and may also be considered less readable.
Source: Exercism rust/leap