Enums

Using enums to define different variants.

Beginner⏱️ 35 min📚 Prerequisites: 1

Enums

An enum allows us to define a type that can have multiple possible variants.

Basic Enum

RUST
enum IpAddrKind {
    V4,
    V6,
}

Using Enums

RUST
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;

Enums with Data

RUST
enum IpAddr {
    V4(String),
    V6(String),
}

let home = IpAddr::V4(String::from("127.0.0.1"));

Different Types of Data

RUST
enum IpAddr {
    V4(u8, u8, u8, u8),
    V6(String),
}

Option Enum

Rust has no null! Instead, we use the Option<T> enum:

RUST
enum Option<T> {
    Some(T),
    None,
}

let some_number = Some(5);
let absent_number: Option<i32> = None;

Match Expression

RUST
match some_value {
    IpAddr::V4(addr) => println!("IPv4: {}", addr),
    IpAddr::V6(addr) => println!("IPv6: {}", addr),
}

Code Examples

Basic Enum

Enum definition and usage

RUST
enum IpAddrKind {
    V4,
    V6,
}
fn main() {
    let four = IpAddrKind::V4;
    let six = IpAddrKind::V6;
    route(four);
    route(six);
}
fn route(ip_kind: IpAddrKind) {
    match ip_kind {
        IpAddrKind::V4 => println!("IPv4 address"),
        IpAddrKind::V6 => println!("IPv6 address"),
    }
}

Explanation:

An enum allows us to define a type with multiple possible values. We reference enum variants with the `::` syntax.

Enum with Data

Enum variants with data

RUST
enum IpAddr {
    V4(String),
    V6(String),
}
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
fn main() {
    let home = IpAddr::V4(String::from("127.0.0.1"));
    let loopback = IpAddr::V6(String::from("::1"));
    let msg = Message::Move { x: 10, y: 20 };
}

Explanation:

Enum variants can contain data. This allows us to store different types and amounts of data in each variant.

Option Enum

Using Option instead of null

RUST
fn main() {
    let some_number = Some(5);
    let some_string = Some("a string");
    let absent_number: Option<i32> = None;
    match some_number {
        Some(value) => println!("Value: {}", value),
        None => println!("No value"),
    }
}

Explanation:

The Option<T> enum is Rust's way of handling null values. Some(T) contains a value, None indicates no value.

Blockchain: Transaction Type Enum

Using enums for different transaction types

RUST
enum TransactionType {
    Transfer { from: String, to: String, amount: u64 },
    ContractCall { contract: String, method: String, args: Vec<String> },
    Stake { validator: String, amount: u64 },
    Unstake { validator: String, amount: u64 },
}
fn process_transaction(tx: TransactionType) {
    match tx {
        TransactionType::Transfer { from, to, amount } => {
            println!("Transfer {} tokens from {} to {}", amount, from, to);
        },
        TransactionType::ContractCall { contract, method, args } => {
            println!("Calling {}.{} with {:?}", contract, method, args);
        },
        TransactionType::Stake { validator, amount } => {
            println!("Staking {} tokens to validator {}", amount, validator);
        },
        TransactionType::Unstake { validator, amount } => {
            println!("Unstaking {} tokens from validator {}", amount, validator);
        },
    }
}
fn main() {
    let tx1 = TransactionType::Transfer {
        from: String::from("0xAlice"),
        to: String::from("0xBob"),
        amount: 100,
    };
    process_transaction(tx1);
    let tx2 = TransactionType::Stake {
        validator: String::from("0xValidator1"),
        amount: 1000,
    };
    process_transaction(tx2);
}

Explanation:

Enums are perfect for representing different transaction types in blockchain. Each variant can hold different data, and pattern matching handles each type safely.

Blockchain: Block Status Enum

Using enums for block states

RUST
enum BlockStatus {
    Pending,
    Confirmed { confirmations: u32 },
    Orphaned,
    Finalized,
}
fn get_status_message(status: BlockStatus) -> String {
    match status {
        BlockStatus::Pending => String::from("Block is pending confirmation"),
        BlockStatus::Confirmed { confirmations } => {
            format!("Block confirmed with {} confirmations", confirmations)
        },
        BlockStatus::Orphaned => String::from("Block was orphaned (not in main chain)"),
        BlockStatus::Finalized => String::from("Block is finalized (cannot be reverted)"),
    }
}
fn main() {
    let status1 = BlockStatus::Pending;
    let status2 = BlockStatus::Confirmed { confirmations: 6 };
    println!("{}", get_status_message(status1));
    println!("{}", get_status_message(status2));
}

Explanation:

Block status can be represented as an enum. This makes the state explicit and prevents invalid states. Pattern matching ensures all cases are handled.

Exercises

Creating an Enum

Create a Direction enum with North, South, East, West variants!

Easy

Starter Code:

RUST
fn main() {
}

Blockchain: Transaction Status

Create a TransactionStatus enum for blockchain!

Easy

Starter Code:

RUST
fn main() {
}