ExamplesBy LevelBy TopicLearning Paths
025 Fundamental GitHub

025 — Leap Year

Functional Programming

Tutorial

The Problem

Determine whether a year is a leap year using the Gregorian calendar rule: a year is a leap year if it is divisible by 400, or divisible by 4 but not by 100. Express this as a single boolean expression and verify on representative examples.

🎯 Learning Outcomes

  • • Express multi-condition boolean logic with || and && and correct operator precedence
  • • Understand the three-tier hierarchy: 400 overrides 100, which overrides 4
  • • Use Rust's u32 type for years with % modulo operator
  • • Compare directly with OCaml's mod and <> operators
  • • Write test cases covering each branch of the rule
  • • Recognise that a single-expression implementation is clearer than nested if/else
  • Code Example

    #![allow(clippy::all)]
    // Gregorian leap year rule: divisible by 400, or by 4 but not by 100
    pub fn is_leap_year(year: u32) -> bool {
        (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_divisible_by_400_is_leap() {
            assert!(is_leap_year(2000));
            assert!(is_leap_year(1600));
        }
    
        #[test]
        fn test_divisible_by_100_not_400_is_not_leap() {
            assert!(!is_leap_year(1900));
            assert!(!is_leap_year(1800));
        }
    
        #[test]
        fn test_divisible_by_4_not_100_is_leap() {
            assert!(is_leap_year(2004));
            assert!(is_leap_year(2024));
        }
    
        #[test]
        fn test_not_divisible_by_4_is_not_leap() {
            assert!(!is_leap_year(2001));
            assert!(!is_leap_year(2003));
        }
    }

    Key Differences

    AspectRustOCaml
    Modulo%mod
    Equality== 0= 0
    Not equal!= 0<> 0
    Typeu32int
    Boolean ops&&, \|\|&&, \|\|
    Expression lengthSameSame

    The leap year problem is one of the simplest demonstrations of translating between Rust and OCaml — the algorithm is identical, only the surface syntax changes. The canonical single-expression form is preferred over branching because it maps directly to the mathematical definition.

    OCaml Approach

    let leap_year year = (year mod 400 = 0) || (year mod 4 = 0 && year mod 100 <> 0) is identical in structure. The differences are purely syntactic: mod vs %, = vs ==, <> vs !=. Both implementations are equivalent, correct, and O(1).

    Full Source

    #![allow(clippy::all)]
    // Gregorian leap year rule: divisible by 400, or by 4 but not by 100
    pub fn is_leap_year(year: u32) -> bool {
        (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0)
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_divisible_by_400_is_leap() {
            assert!(is_leap_year(2000));
            assert!(is_leap_year(1600));
        }
    
        #[test]
        fn test_divisible_by_100_not_400_is_not_leap() {
            assert!(!is_leap_year(1900));
            assert!(!is_leap_year(1800));
        }
    
        #[test]
        fn test_divisible_by_4_not_100_is_leap() {
            assert!(is_leap_year(2004));
            assert!(is_leap_year(2024));
        }
    
        #[test]
        fn test_not_divisible_by_4_is_not_leap() {
            assert!(!is_leap_year(2001));
            assert!(!is_leap_year(2003));
        }
    }
    ✓ Tests Rust test suite
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[test]
        fn test_divisible_by_400_is_leap() {
            assert!(is_leap_year(2000));
            assert!(is_leap_year(1600));
        }
    
        #[test]
        fn test_divisible_by_100_not_400_is_not_leap() {
            assert!(!is_leap_year(1900));
            assert!(!is_leap_year(1800));
        }
    
        #[test]
        fn test_divisible_by_4_not_100_is_leap() {
            assert!(is_leap_year(2004));
            assert!(is_leap_year(2024));
        }
    
        #[test]
        fn test_not_divisible_by_4_is_not_leap() {
            assert!(!is_leap_year(2001));
            assert!(!is_leap_year(2003));
        }
    }

    Exercises

  • Implement days_in_february(year: u32) -> u32 returning 29 for leap years and 28 otherwise.
  • Write is_leap_year_julian(year: u32) -> bool using only the 4-year rule (used before 1582).
  • Count how many leap years are in the range 1900..=2100 using filter and count.
  • Find the next leap year after a given year using (year+1..).find(|&y| is_leap_year(y)).
  • In OCaml, write years_until_next_leap : int -> int that returns how many years until the next leap year.
  • Open Source Repos