Resistor Color in Cairo: Complete Solution & Deep Dive Guide

Pyramids visible over buildings and street traffic

The Ultimate Guide to Cairo Enums: Solving Resistor Color Logic from Scratch

Mastering resistor color coding in Cairo involves using enums to represent each color and a match statement to map them to their corresponding integer values. This approach leverages Cairo's strong type system to ensure code safety and readability, making it the ideal method for handling fixed sets of data like electronic color codes.

Ever found yourself staring at a tiny electronic component, a resistor, wrapped in a series of colored bands, and felt like you were trying to decipher an ancient, cryptic language? You're not alone. For hobbyists and engineers alike, these colors are a compact code representing a resistor's value, essential for building any circuit. But what if we could translate this hardware puzzle into a software masterpiece?

This isn't just an electronics lesson; it's a gateway to mastering one of the most powerful and elegant features in the Cairo programming language: Enums. In this deep dive, we'll take the abstract concept of resistor colors and build a robust, type-safe Cairo program from the ground up. You'll not only solve a practical problem but also gain a profound understanding of why Cairo's design principles are critical for building secure and reliable smart contracts on Starknet.


What is the Resistor Color Code Challenge?

At its core, the Resistor Color Code is a system used to denote the value of a resistor. Since resistors are often too small to have numbers printed on them, a standardized color system is used instead. For our challenge, which is based on a foundational module from the exclusive kodikra.com learning path, we focus on the numerical value of each color.

The system assigns a specific digit, from 0 to 9, to a particular color. The standard mnemonic "Big Boys Race Our Young Girls But Violet Generally Wins" helps remember the sequence.

  • Black: 0
  • Brown: 1
  • Red: 2
  • Orange: 3
  • Yellow: 4
  • Green: 5
  • Blue: 6
  • Violet: 7
  • Grey: 8
  • White: 9

Our task is to build two primary functions in Cairo:

  1. A function that takes a single color and returns its corresponding integer value.
  2. A function that returns a list of all available colors in their correct order.

This seemingly simple task is the perfect vehicle for exploring Cairo's powerful type system, particularly enums and pattern matching, which are fundamental concepts for any aspiring Cairo developer.


Why Use Cairo for This Problem?

You might wonder, "Why use a cutting-edge smart contract language like Cairo for a classic electronics problem?" The answer lies not in the problem's complexity, but in what its solution reveals about the language's philosophy and strengths.

1. Unmatched Type Safety: Cairo is a statically-typed language, meaning the type of every variable is known at compile time. By defining our colors as an enum, we create a new, distinct type called Color. This prevents a whole class of bugs. You can't accidentally pass a string like "Reddish" or a number like 12 to a function expecting a Color. The compiler will stop you, enforcing correctness before your code ever runs.

2. Expressive and Exhaustive Pattern Matching: Cairo's match statement is far more powerful than a simple if-else chain or a switch statement in other languages. When you match on an enum, the Cairo compiler enforces exhaustiveness. This means you must handle every single possible variant of the enum. If you forget a color, the code won't compile. This feature is a lifesaver in smart contracts, where unhandled states can lead to catastrophic vulnerabilities.

3. Readability and Maintainability: Using an enum like Color::Red makes the code self-documenting. It's immediately clear what the value represents, unlike using a "magic number" like 2. If you need to add a new color or change a value, you only need to modify the enum definition and the relevant match arm, and the compiler will guide you to all the places that need updating.

4. Foundation for Complex Smart Contracts: While resistor colors are simple, the pattern is universal in smart contract development. You might use an enum to represent the status of a loan (Pending, Active, Repaid, Defaulted), the type of an NFT (Common, Rare, Legendary), or the state of a vote (Proposed, Open, Closed, Executed). Mastering enums here builds the mental model needed for these more complex, high-stakes applications.


How to Solve the Resistor Color Problem in Cairo

Let's roll up our sleeves and build the solution step-by-step. We will define our types, implement the core logic, and ensure our code is clean, efficient, and idiomatic Cairo.

Prerequisites: Setting Up Your Environment

Before we begin, ensure you have the Cairo toolchain installed. The primary tool you'll need is scarb, the Cairo package manager. If you haven't set it up, you can find instructions on the official Starknet documentation.

You can create a new project for this module with a simple command:


$ scarb new resistor_color
     Created `resistor_color` package.

Now, navigate into the directory (cd resistor_color) and open the src/lib.cairo file. This is where we'll write our code.

Step 1: The Heart of the Solution - Defining the `Color` Enum

The most crucial step is to model our data correctly. Since we have a fixed, known set of colors, an enum is the perfect tool. An enum (enumeration) is a custom type that can be one of several possible variants.

Here is the Cairo definition for our Color enum. We'll add some helpful attributes using the #[derive] macro, which we'll explain shortly.

// src/lib.cairo

#[derive(Copy, Drop, PartialEq, Debug)]
pub enum Color {
    Black,
    Brown,
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Violet,
    Grey,
    White,
}

Let's break down the #[derive(...)] attributes:

  • Copy: This allows values of type Color to be copied implicitly. Since our enum variants don't contain any complex data, they are cheap to copy.
  • Drop: This trait is required for types to be able to go out of scope. It tells the compiler how to clean up the value, which is trivial in this case.
  • PartialEq: This allows us to compare two Color values for equality using the == operator. This is useful for testing.
  • Debug: This allows the enum to be printed in a human-readable format, which is incredibly helpful for debugging purposes.

Step 2: Implementing the `color_code` Function

Now we need a function that maps each Color variant to its integer value. This is the perfect use case for a match statement.

The function signature will be fn color_code(color: Color) -> u8. It takes one argument, color of type Color, and returns a u8 (an 8-bit unsigned integer), which is more than enough to hold values from 0 to 9.


pub fn color_code(color: Color) -> u8 {
    match color {
        Color::Black => 0,
        Color::Brown => 1,
        Color::Red => 2,
        Color::Orange => 3,
        Color::Yellow => 4,
        Color::Green => 5,
        Color::Blue => 6,
        Color::Violet => 7,
        Color::Grey => 8,
        Color::White => 9,
    }
}

Notice the elegance of this code. It's clear, concise, and safe. If we were to add a new color to the Color enum, the Cairo compiler would produce an error here, reminding us that our match statement is no longer exhaustive. This prevents runtime errors and ensures all cases are handled.

Here's a visual representation of the logic flow for this function.

    ● Start: Input `Color` value
    │
    ▼
  ┌───────────────────┐
  │ `color_code` func │
  └─────────┬─────────┘
            │
            ▼
    ◆ Match `color`?
   ╱         │         ╲
  ├─Color::Black ⟶ Return 0
  ├─Color::Brown ⟶ Return 1
  ├─Color::Red   ⟶ Return 2
  ├─  ... (and so on)
  └─Color::White ⟶ Return 9
            │
            ▼
    ● End: Output `u8` value

Step 3: Implementing the `colors` Function

The final requirement is a function that returns a complete list of all the colors. In Cairo, the standard dynamic list type is an Array<T>. Our function will construct and return an Array<Color>.


use array::ArrayTrait;

// ... (enum and color_code function above)

pub fn colors() -> Array<Color> {
    let mut all_colors = ArrayTrait::new();
    all_colors.append(Color::Black);
    all_colors.append(Color::Brown);
    all_colors.append(Color::Red);
    all_colors.append(Color::Orange);
    all_colors.append(Color::Yellow);
    all_colors.append(Color::Green);
    all_colors.append(Color::Blue);
    all_colors.append(Color::Violet);
    all_colors.append(Color::Grey);
    all_colors.append(Color::White);
    all_colors
}

In this function, we first create a new, empty array called all_colors. We then append each variant of the Color enum to it in the correct order. Finally, we return the populated array.

Full Solution Code (`src/lib.cairo`)

Here is the complete code for our library file. It's clean, organized, and ready to be compiled and tested.


use array::{ArrayTrait, SpanTrait};

/// Represents the standard colors used on resistors.
#[derive(Copy, Drop, PartialEq, Debug)]
pub enum Color {
    Black,
    Brown,
    Red,
    Orange,
    Yellow,
    Green,
    Blue,
    Violet,
    Grey,
    White,
}

/// Maps a given resistor color to its corresponding numerical value.
///
/// # Arguments
///
/// * `color` - A value of the `Color` enum.
///
/// # Returns
///
/// A `u8` integer representing the color's code (0-9).
pub fn color_code(color: Color) -> u8 {
    match color {
        Color::Black => 0,
        Color::Brown => 1,
        Color::Red => 2,
        Color::Orange => 3,
        Color::Yellow => 4,
        Color::Green => 5,
        Color::Blue => 6,
        Color::Violet => 7,
        Color::Grey => 8,
        Color::White => 9,
    }
}

/// Returns an array containing all resistor colors in order of their value.
///
/// # Returns
///
/// An `Array<Color>` with all 10 color variants.
pub fn colors() -> Array<Color> {
    let mut all_colors = ArrayTrait::new();
    all_colors.append(Color::Black);
    all_colors.append(Color::Brown);
    all_colors.append(Color::Red);
    all_colors.append(Color::Orange);
    all_colors.append(Color::Yellow);
    all_colors.append(Color::Green);
    all_colors.append(Color::Blue);
    all_colors.append(Color::Violet);
    all_colors.append(Color::Grey);
    all_colors.append(Color::White);
    all_colors
}

This ASCII diagram illustrates the overall program structure and data flow.

    ● Define Types
    │
    ├───► ┌───────────────┐
    │     │ `enum Color`  │
    │     └───────────────┘
    │
    ▼
    ● Define Logic
    │
    ├───► ┌──────────────────┐
    │     │ `color_code()`   │
    │     │ (Color -> u8)    │
    │     └──────────┬───────┘
    │                │
    ├───► ┌───────────────┐
    │     │ `colors()`    │
    │     │ (() -> Array) │
    │     └───────────────┘
    │
    ▼
    ● Execute/Test
    │
    ├───► Call `color_code(Color::Red)` ───► Get `2`
    │
    └───► Call `colors()` ───────────────► Get `[Black, Brown, ...]`

Alternative Approaches and Why Enums are Superior

While using an enum and match is the most idiomatic and safest way to solve this in Cairo, a developer coming from another language might consider other options. Let's explore them and understand why they are less suitable for Cairo.

1. Using Plain Integers (e.g., `u8`):

One could represent colors with raw integers: 0 for Black, 1 for Brown, etc. The `color_code` function would simply return the integer itself. However, this approach is fraught with peril. It introduces "magic numbers" into the codebase, making it hard to read. More dangerously, a function expecting a color code could be passed any `u8` value, like `50`, which has no meaning in our context. This completely bypasses the type system's protection.

2. Using Structs and an Array:

You could define a struct containing the color's name and value, then store these in an array. This is better than raw integers but is still less efficient and safe than an enum for a fixed set of values. Searching the array for a specific color would be slower than the direct jump-table-like optimization the compiler can perform for a match statement.

Here is a comparison table summarizing the trade-offs:

Approach Pros Cons
Enum with Match (Idiomatic) - Maximum type safety
- Compile-time exhaustiveness checks
- Highly readable and self-documenting
- Very performant (compiler optimized)
- Slightly more verbose for initial setup
Plain Integers (`u8`) - Appears simple at first - No type safety (any `u8` is valid)
- Prone to "magic number" bugs
- Hard to read and maintain
- Error-prone
Array of Structs - Groups data logically - Slower lookup performance
- No compile-time guarantee of completeness
- More memory overhead
- Not idiomatic for fixed sets

For these reasons, the enum and match combination is not just a stylistic choice in Cairo; it's a deliberate design decision that leverages the language's core strengths to produce more robust and secure code.


Frequently Asked Questions (FAQ)

What exactly is an enum in Cairo?

An enum, or enumeration, is a custom data type that allows you to define a set of named variants. A variable of that enum type can only hold one of its variants at a time. It's a way to tell the compiler about a fixed set of possibilities, enhancing type safety and code clarity. In our case, a variable of type Color can be Color::Black, Color::Brown, etc., but nothing else.

Why is pattern matching better than a long `if/else if` chain?

Pattern matching with match is superior for two main reasons. First, the Cairo compiler enforces exhaustiveness, meaning you must handle every possible enum variant. An if/else if chain doesn't have this guarantee, making it easy to forget a case. Second, match statements are often more performant as the compiler can optimize them into a highly efficient jump table, which is faster than a sequential series of boolean checks.

What does `#[derive(Copy, Drop, PartialEq, Debug)]` actually do?

#[derive(...)] is a macro that automatically generates boilerplate code for certain common traits.

  • Copy and Drop are memory management traits that tell the compiler how to handle the data. For simple enums, they are cheap to implement.
  • PartialEq generates the code needed to compare two instances of the enum for equality (e.g., Color::Red == Color::Red).
  • Debug generates code to allow the enum to be printed in a developer-friendly format, which is essential for debugging.

How would I add a new color, like "Pink", to this system?

It's a straightforward, compiler-guided process. First, you would add Pink as a new variant to the Color enum. As soon as you try to compile, the compiler will show an error in the color_code function, stating that the match statement is not exhaustive. You would then add a new arm, Color::Pink => 10,. Finally, you would add all_colors.append(Color::Pink); to the colors function. This safety net is a key benefit of the enum approach.

Is this a realistic problem for smart contracts?

While you probably won't be decoding resistor colors on-chain, the underlying pattern is extremely common and critical. Smart contracts constantly manage state, which can often be represented by an enum: LoanStatus::{Pending, Active, Repaid}, VoteState::{Open, Closed, Executed}, GameStatus::{NotStarted, InProgress, Finished}. The principles of type safety and exhaustive state handling learned here are directly applicable and essential for writing secure smart contracts.

Where can I learn more fundamental Cairo concepts?

This module is part of a structured learning curriculum. To build a solid foundation and explore more advanced topics, we highly recommend following our complete Cairo 2 learning roadmap. For a comprehensive overview of the language, check out our ultimate guide to Cairo programming.


Conclusion: From Colors to Contracts

We've successfully translated the physical world of resistor color codes into a clean, safe, and efficient Cairo program. More importantly, we've used this simple problem as a lens to explore the profound benefits of Cairo's design philosophy. The combination of strong enums and exhaustive pattern matching isn't just a feature; it's a safety-critical tool that helps developers write code that is correct by construction.

The skills you've honed today—defining custom types, handling all possible states, and writing readable, self-documenting code—are the very bedrock of professional smart contract development. As you continue your journey, you'll find this pattern repeating in more complex and fascinating ways. Keep building, keep learning, and continue to master the art of writing provably correct code with Cairo.

Disclaimer: The code and concepts in this article are based on Cairo version 2.6.3 and the Scarb 0.8.4 package manager. The Cairo language and its ecosystem are rapidly evolving, so syntax and features may change in future versions.


Published by Kodikra — Your trusted Cairo learning resource.