Master Weather Report in Cairo: Complete Learning Path

ferries along the city river banks near bridge with crossing cars

Master Weather Report in Cairo: Complete Learning Path

This guide provides a comprehensive walkthrough of the "Weather Report" concept, a fundamental pattern in Cairo for modeling complex states. You will learn to leverage Cairo's powerful type system, including enums and pattern matching, to write safer, more expressive, and robust smart contracts for the StarkNet ecosystem.


Have you ever tried to manage an object that can be in multiple, distinct states within your code? Perhaps you used a series of boolean flags or magic numbers, only to find your logic tangled in a web of `if-else` statements. This approach is not only confusing but also dangerously error-prone, especially in the unforgiving world of smart contracts where a single bug can have catastrophic consequences.

This is a common pain point for developers. The "Weather Report" module from the exclusive kodikra.com curriculum is designed to solve this exact problem. It teaches you how to model complex, real-world scenarios cleanly and safely using Cairo's most expressive features. By the end of this guide, you will master the art of representing intricate states, ensuring your code is not just functional but also verifiable, readable, and secure.


What is the "Weather Report" Pattern in Cairo?

In the context of the kodikra learning path, the "Weather Report" pattern isn't a specific library but a foundational programming paradigm. It revolves around using Cairo's algebraic data types, primarily Enumerations (enum), to represent a value that can be one of several possible variants. Each variant can hold different associated data, allowing for incredibly flexible and precise data modeling.

Imagine you need to represent the weather. It can be sunny, cloudy, rainy, or snowy. Some of these states have extra information: cloudiness might have a percentage, and rain might have an intensity and duration. Instead of juggling separate variables, Cairo lets you encapsulate all this logic into a single, cohesive type.

This pattern is brought to life by the match expression, a powerful control flow construct that allows you to destructure an enum and execute different code for each possible variant. Critically, the Cairo compiler enforces exhaustiveness, meaning you must handle every single variant, eliminating an entire class of bugs related to unhandled states.

Core Components of the Pattern

  • enum: The core data type. It defines a set of named variants. For example, enum Weather { Sunny, Cloudy, Rainy }.
  • Enum Variants with Data: Variants can be simple labels (like Sunny) or they can carry associated data of different types (e.g., Cloudy(u8) to store cloud cover percentage, or Rainy { intensity: u8, duration_hours: u16 } to store structured data).
  • match Expression: The mechanism for inspecting an enum instance. It allows you to branch your code's logic based on the specific variant and to extract the data associated with it.

Why is This State-Modeling Pattern Crucial for Smart Contracts?

The design of smart contracts demands the highest levels of security and correctness. The "Weather Report" pattern, leveraging enums and pattern matching, is not just a stylistic choice in Cairo; it's a critical tool for achieving robustness on-chain.

1. Unmatched Type Safety

By defining all possible states in an enum, you create a closed world of possibilities. It becomes impossible at the type level to represent an invalid state. You can't accidentally have a state that is both "Pending" and "Completed." The compiler guarantees that a variable of your enum type can only hold one of the defined variants at any given time.

2. Elimination of Logic Errors with Exhaustive Matching

The Cairo compiler's requirement for exhaustive `match` statements is a game-changer. If you add a new variant to an enum (e.g., adding `Hailing` to your `Weather` enum), the compiler will flag every `match` statement that doesn't handle this new case as a compile-time error. This prevents you from deploying code that forgets to handle a possible state, a common source of bugs in other languages.

3. Enhanced Code Readability and Maintainability

Code that uses this pattern is self-documenting. A function signature that takes a LoanStatus enum is far clearer than one that takes a u8 where `0` means "Active," `1` means "Repaid," and `2` means "Liquidated." When another developer (or your future self) reads the code, the intent is immediately obvious, making the contract easier to audit and maintain.

4. Gas Efficiency and Optimized Storage

The Cairo Virtual Machine is optimized to handle enums efficiently. The compiler will often pack the enum's variant identifier (the tag) and its associated data into a compact memory layout. This can be more gas-efficient than managing multiple separate storage variables to track the same state, especially as the complexity of the state grows.


How to Implement the Weather Report Pattern in Cairo

Let's dive into the practical implementation. We'll build a complete example from defining the enum to processing it with a match expression. This process is central to the challenges you'll face in the kodikra module.

Step 1: Defining the Enum in a Cairo File

First, create a new Cairo project using Scarb, the official Cairo package manager. If you don't have it, you can install it via `asdf` or by following the official StarkNet documentation.


$ scarb new weather_project
$ cd weather_project

Now, open src/lib.cairo and define a detailed WeatherCondition enum. Notice how variants can be simple, hold a single value, or hold a named struct-like payload.


#[derive(Drop, Serde)]
enum WeatherCondition {
    Sunny,
    Cloudy(u8), // Represents cloud cover percentage (0-100)
    Rainy {
        intensity: u8, // mm per hour
        duration_hours: u16,
    },
    Windy {
        speed_kph: u16,
        gust_speed_kph: u16,
    },
}

Here, #[derive(Drop, Serde)] are attributes that tell the compiler to automatically generate code for memory management (Drop) and serialization/deserialization (Serde), which are often necessary for custom types.

Step 2: Creating a Function to Process the Enum

The real power comes from processing this enum. Let's write a function that takes a WeatherCondition and returns a human-readable description as a felt252. This is where the match expression shines.


fn generate_weather_alert(condition: WeatherCondition) -> felt252 {
    match condition {
        WeatherCondition::Sunny => {
            'Perfect sunny day! Wear sunscreen.'
        },
        WeatherCondition::Cloudy(percentage) => {
            if percentage > 80 {
                'Very overcast. Might drizzle.'
            } else if percentage > 50 {
                'Partly cloudy skies.'
            } else {
                'A few scattered clouds.'
            }
        },
        WeatherCondition::Rainy { intensity, duration_hours } => {
            if intensity > 10 {
                'Heavy rain warning! Stay indoors.'
            } else {
                'Light rain expected. Bring an umbrella.'
            }
        },
        WeatherCondition::Windy { speed_kph, gust_speed_kph } => {
            if gust_speed_kph > 80 {
                'High wind advisory! Secure loose objects.'
            } else {
                'It is a bit breezy today.'
            }
        },
    }
}

In the match block, we handle each variant:

  • WeatherCondition::Sunny is a simple match.
  • WeatherCondition::Cloudy(percentage) binds the inner u8 value to a new variable named percentage, which we can then use in our logic.
  • WeatherCondition::Rainy { intensity, duration_hours } destructures the payload, binding the fields to variables of the same name. We could also rename them, e.g., Rainy { intensity: i, .. } to bind intensity to i and ignore the rest with ...

ASCII Art Diagram: The `match` Expression Flow

This diagram illustrates how a `match` expression directs the flow of your program based on the enum variant.

    ● Input: `WeatherCondition` instance
    │
    ▼
  ┌───────────────────────────┐
  │ `match condition { ... }` │
  └────────────┬──────────────┘
               │
      ╭────────┴─────────╮
      │ Is it `Sunny`?   │
      Yes ⟶ returns "Perfect sunny day!..." ⟶ ● End
      │ No
      ▼
      ╭────────────────────╮
      │ Is it `Cloudy(p)`? │
      Yes ⟶ binds `p`, executes logic ⟶ ● End
      │ No
      ▼
      ╭───────────────────────────────╮
      │ Is it `Rainy { i, d }`?       │
      Yes ⟶ binds `i`, `d`, executes logic ⟶ ● End
      │ No
      ▼
      ╭───────────────────────────────╮
      │ Is it `Windy { s, g }`?       │
      Yes ⟶ binds `s`, `g`, executes logic ⟶ ● End
      ╰───────────────────────────────╯

Step 3: Compiling and Running

To test this, we can add a main function to our src/lib.cairo for local execution (note: main functions are for off-chain running, not for contracts). Add the `main` function and the necessary imports.


use serde::Serde;

#[derive(Drop, Serde)]
enum WeatherCondition {
    Sunny,
    Cloudy(u8),
    Rainy {
        intensity: u8,
        duration_hours: u16,
    },
    Windy {
        speed_kph: u16,
        gust_speed_kph: u16,
    },
}

fn generate_weather_alert(condition: WeatherCondition) -> felt252 {
    // ... (match expression from before)
    match condition {
        WeatherCondition::Sunny => 'Perfect sunny day! Wear sunscreen.',
        WeatherCondition::Cloudy(percentage) => {
            if percentage > 80 {
                'Very overcast. Might drizzle.'
            } else if percentage > 50 {
                'Partly cloudy skies.'
            } else {
                'A few scattered clouds.'
            }
        },
        WeatherCondition::Rainy { intensity, duration_hours } => {
            if intensity > 10 {
                'Heavy rain warning! Stay indoors.'
            } else {
                'Light rain expected. Bring an umbrella.'
            }
        },
        WeatherCondition::Windy { speed_kph, gust_speed_kph } => {
            if gust_speed_kph > 80 {
                'High wind advisory! Secure loose objects.'
            } else {
                'It is a bit breezy today.'
            }
        },
    }
}

fn main() {
    let current_weather = WeatherCondition::Rainy { intensity: 12, duration_hours: 3 };
    let alert = generate_weather_alert(current_weather);
    println!("Today's Alert: {}", alert);

    let another_day = WeatherCondition::Cloudy(65);
    let another_alert = generate_weather_alert(another_day);
    println!("Another Alert: {}", another_alert);
}

Now, run it from your terminal:


$ scarb cairo-run
Today's Alert: Heavy rain warning! Stay indoors.
Another Alert: Partly cloudy skies.

This command compiles and executes your Cairo code, demonstrating the pattern in action.

ASCII Art Diagram: Enum Data Structure

This diagram visualizes how an enum is conceptually structured. It's a single type that can take on different "shapes" or "forms".

  ┌──────────────────┐
  │ `WeatherCondition` │ (Enum Type)
  └─────────┬────────┘
            │
            ▼
     ◆ Can be one of...
    ╱         |          |          ╲
   ╱          |          |           ╲
  ▼           ▼          ▼            ▼
┌───────┐ ┌─────────┐ ┌──────────┐ ┌──────────┐
│ Sunny │ │ Cloudy  │ │  Rainy   │ │  Windy   │ (Variants)
└───────┘ └────┬────┘ └────┬─────┘ └────┬─────┘
               │           │            │
               ▼           ▼            ▼
             [ u8 ]  ┌───────────┐  ┌───────────┐
                     │intensity:u8│  │ speed:u16 │ (Associated Data)
                     │duration:u16│  │ gust:u16  │
                     └───────────┘  └───────────┘

Where is this Pattern Used in Real-World Smart Contracts?

The "Weather Report" pattern is ubiquitous in professional smart contract development. Its ability to model states clearly and safely makes it invaluable.

  • DeFi Protocols: A lending protocol might use an enum LoanStatus { Active, Repaid, Defaulted, Liquidating { amount: u256 } }. The state transitions are managed explicitly, and the Liquidating state can carry the exact amount being liquidated.
  • On-Chain Gaming & NFTs: An NFT representing a game character could have an enum CharacterState { Idle, Questing(u32), InCombat { enemy_id: u252 }, Crafting { item_id: u128 } }. This ensures the character cannot be both `Idle` and `InCombat` simultaneously.
  • Governance Systems: A DAO proposal's lifecycle can be modeled perfectly with ProposalState { Pending, Active, Succeeded, Defeated, Queued, Executed }. This makes the logic for voting, queuing, and executing proposals much cleaner.
  • Supply Chain & Logistics: Tracking a package can be modeled with ShipmentStatus { Created, InTransit { location_hash: felt252, timestamp: u64 }, Delivered, Canceled }. The `InTransit` variant can even hold data about the package's last known location.

Pros & Cons of the Enum/Match Pattern

Like any programming pattern, it's essential to understand its trade-offs to use it effectively. This pattern is overwhelmingly positive for state management in Cairo, but awareness of its characteristics is key.

Pros (Advantages) Cons & Risks (Considerations)
  • Compile-Time Safety: The compiler's exhaustiveness check prevents unhandled state bugs.
  • High Readability: Code becomes self-documenting and easier to reason about.
  • Encapsulation: All possible states and their associated data are defined in one place.
  • Prevents Invalid States: It's impossible to construct a state that isn't defined in the enum.
  • Efficient: The Cairo compiler is designed to optimize the storage and handling of enums.
  • Rigidity: Adding a new state requires modifying the enum and all `match` statements, which triggers a recompile. This is a feature for safety but can be cumbersome during rapid prototyping.
  • Can Become Large: An enum with dozens of variants can become unwieldy, though this often points to a need for refactoring the state logic itself.
  • Slight Overhead: For a simple true/false state, a bool is marginally more direct and gas-efficient than an enum { True, False }.

The Kodikra Learning Path Module

Now that you have a deep understanding of the theory and application, you are ready to put your knowledge into practice. The following module in our exclusive kodikra.com curriculum is designed to solidify these concepts through hands-on coding.

  • Learn Weather Report step by step: In this challenge, you will implement a system to process and report on various weather conditions using the enum and match patterns discussed here. This is a crucial step in mastering Cairo's type system.

Completing this module will give you the confidence to model any complex state machine you might encounter while building powerful and secure applications on StarkNet.


Frequently Asked Questions (FAQ)

What is the difference between a `struct` and an `enum` in Cairo?

A struct is a product type; it combines multiple different types into a single unit (an "AND" relationship). For example, struct User { id: u32, address: ContractAddress } means a User has an ID and an address. An enum is a sum type; it represents a value that can be one of several different types (an "OR" relationship). enum Result { Ok(u256), Err(felt252) } means a Result is either Ok or an Err, but never both.

Why is the `match` statement's exhaustiveness so important for security?

In smart contracts, unhandled states can lead to vulnerabilities. For example, if a contract managing funds has states `Locked` and `Unlocked` but a developer forgets to handle the `Locked` state in a withdrawal function, funds could be stuck forever. Exhaustiveness forces the developer to explicitly define the behavior for every possible state, preventing such oversights at compile time, long before deployment.

Can I nest enums within other enums or structs?

Yes, absolutely. This is a powerful feature for modeling even more complex data. For example, you could have a Rainy variant that contains a PrecipitationType enum: enum Weather { Rainy(PrecipitationType) } where enum PrecipitationType { Drizzle, Steady, Downpour }. This allows for highly granular and organized state definitions.

How does this pattern affect the gas cost of my smart contract?

Generally, this pattern is gas-efficient. The Cairo compiler stores an enum as a "tag" (a small integer identifying the variant) plus the data for that variant. This is often more compact than using multiple boolean flags or integer codes stored in separate storage slots. The `match` statement itself compiles down to efficient branching logic in Cairo Assembly (CASM).

What happens if I don't want to handle every case in a `match`?

The compiler will force you to be exhaustive. However, if you have many cases where you want to do nothing or perform a default action, you can use the wildcard underscore _ as the final arm of your match. For example: match condition { Weather::Sunny => 'Go outside!', _ => 'Stay inside.', }. The _ will match any variant not explicitly listed before it. Use this with caution, as it sacrifices the compile-time check if you later add new variants.

Is this pattern unique to Cairo?

No, this pattern of using algebraic data types and pattern matching is a cornerstone of many modern, safety-focused programming languages like Rust, Swift, Kotlin, and Haskell. Cairo's implementation is heavily inspired by Rust, bringing these proven concepts for robust software development directly to the world of blockchain and zero-knowledge proofs.


Conclusion: Building a Foundation for Robust Contracts

Mastering the "Weather Report" pattern is more than just learning a piece of Cairo syntax; it's about adopting a mindset of precision and safety. By leveraging enums and exhaustive pattern matching, you empower the Cairo compiler to be your vigilant partner in building secure and reliable smart contracts. This pattern transforms complex state management from a source of bugs into a pillar of your application's strength.

You now have the theoretical knowledge and practical examples to tackle complex state modeling challenges. The next logical step is to apply this knowledge in the kodikra learning path. Dive into the exercise, experiment with creating your own complex enums, and build the foundation you need to become an expert Cairo developer.

Disclaimer: All code examples are written for Cairo v2.6.x and Scarb v2.6.x. The Cairo language and its tooling are under active development, and syntax may evolve in future versions.

Back to the complete Cairo Guide


Published by Kodikra — Your trusted Cairo learning resource.