How to Match to Enum

I reviewed the enum section in the book, but I'm still at a loss on how to get back an enum variant based on the results of using mcause::read().cause() reading mcause returns a structure of type <Mcause> which has some implementations. I'm calling the .cause() method which returns <Trap>. This enum has Exception and Interrupt variants which are also enums.
Question: How the heck do I get the enum value based on what was read?

It depends on what you want to do with the enum values and how you want to handle each variant. Here's an example to maybe clear things up:

use riscv::register::mcause::{self, Exception, Interrupt, Trap};

fn main() {
    // cause() returns the enum `Trap`
    let cause: Trap = mcause::read().cause();

    // `Trap` is an enum with two variants.
    // Both of these variants contain another enum as data.
    match cause {
        Trap::Exception(e) => {
            // You could handle this nested enum with another `match` here,
            // but it can be cleaner to factor it out into a function:
            handle_exception(e);
        }
        Trap::Interrupt(i) => {
            // Or if you only need to handle 1 case, use an `if let`:
            if let Interrupt::UserSoft = i {
                println!("The user is soft");
            }
        }
    }
}

fn handle_exception(e: Exception) {
    match e {
        // Handle a bunch of cases here
        Exception::InstructionMisaligned => {}
        Exception::InstructionFault => {}
        Exception::IllegalInstruction => {}
        Exception::Breakpoint => {}
        Exception::LoadMisaligned => {}
        // Maybe you don't care about every case.
        // Use a wildcase `_` for the rest.
        _ => {}
    }
}
4 Likes

Many of these techniques can be combined without using intesmediate values, so you can also write something like this:

use Exception::*;
use Interrupt::*;
match mcause::read().cause() {
    Trap::Exception(InstructionMisaligned) => {}
    Trap::Exception(IllegalInstruction) => {}
    Trap::Exception(_) => {}
    Trap::Interrupt(UserSoft) => {}
    _ => {}
}
7 Likes

Awesome solutions @ArifRoktim and @2e71828. I like seeing the more spelled out version and a shortened version. That helps me on how to handle it and understand what I am doing.

A cool thing I learned the other day reading the docs:

match mcause::read().cause() {
    val @ Trap::Exception(InstructionMisaligned) => { // let value: Trap = val; }
    val @ Trap::Exception(IllegalInstruction) => {}
    val @ Trap::Exception(_) => {}
    val @ Trap::Interrupt(UserSoft) => {}
    _ => {}
}

In this case, val is the enum value being expanded in the corresponding case. I use this when I need to re-use the enum after matching it (saving me a clone)

1 Like

I modified it a bit for testing

// This function handles machine traps due to interrupts or exceptions
fn trap_handler(){
    use mcause::Trap;

    sprintln!("Machine Trap Occurred!");
    match mcause::read().cause(){
        Trap::Exception(exception) => {sprintln!("Exception Trap: {:?}",exception)}
        Trap::Interrupt(interrupt) => {sprintln!("Interrupt Trap: {:?}",interrupt)}
    }
    loop{};
}

Results:

Preparing to Test Trap Handler
Machine Trap Occurred!
Exception Reason: IllegalInstruction