Panicking

panic!()

  • In some cases, when an error occurs we can not do anything to handle it, if the error is something which should not have happened. In other words, if it’s an unrecoverable error.
  • Also when we are not using a feature-rich debugger or proper logs, sometimes we need to debug the code by quitting the program from a specific line of code by printing out a specific message or a value of a variable binding to understand the current flow of the program. For above cases, we can use panic! macro.

panic!() runs thread based. One thread can be panicked, while other threads are running.

01. Quit from a specific line.

fn main() {
    // some code

    // if we need to debug in here
    panic!();
}

// -------------- Compile-time error --------------
thread 'main' panicked at 'explicit panic', src/main.rs:5:5

02. Quit with a custom error message.

#[allow(unused_mut)] // 💡 A lint attribute used to suppress the warning; username variable does not need to be mutable
fn main() {
    let mut username = String::new();

    // some code to get the name
  
    if username.is_empty() {
        panic!("Username is empty!");
    }

    println!("{}", username);
}

// -------------- Compile-time error --------------
thread 'main' panicked at 'Username is empty!', src/main.rs:8:9

03. Quit with the value of code elements.

#[derive(Debug)] // 💡 A lint attribute which use to implement `std::fmt::Debug` to Color
struct Color {
    r: u8,
    g: u8,
    b: u8,
}

#[allow(unreachable_code)] // 💡 A lint attribute used to suppress the warning; unreachable statement
fn main() {
    let some_color: Color;
    
    // some code to get the color. ex
    some_color = Color {r: 255, g: 255, b: 0};

    // if we need to debug in here
    panic!("{:?}", some_color);

    println!(
        "The color = rgb({},{},{})",
        some_color.r, some_color.g, some_color.b
    );
}

// -------------- Compile-time error --------------
thread 'main' panicked at 'Color { r: 255, g: 255, b: 0 }', src/main.rs:16:5

As you can see in the above examples panic!() supports println!() type style arguments. By default, it prints the error message, file path and line & column numbers where the error happens.

unimplemented!()

💡 If your code is having unfinished code sections, there is a standardized macro as unimplemented!() to mark those routes. The program will be panicked with a “not yet implemented” error message, if the program runs through those routes.

// error messages with panic!()
thread 'main' panicked at 'explicit panic', src/main.rs:6:5
thread 'main' panicked at 'Username is empty!', src/main.rs:9:9
thread 'main' panicked at 'Color { r: 255, g: 255, b: 0 }', src/main.rs:17:5

// error messages with unimplemented!()
thread 'main' panicked at 'not yet implemented', src/main.rs:6:5
thread 'main' panicked at 'not yet implemented: Username is empty!', src/main.rs:9:9
thread 'main' panicked at 'not yet implemented: Color { r: 255, g: 255, b: 0 }', src/main.rs:17:5

unreachable!()

This is the standard macro to mark routes that the program should not enter. The program will be panicked with a “‘internal error: entered unreachable code’” error message, if the program entered those routes.

fn main() {
    let level = 22;
    let stage = match level {
        1..=5 => "beginner",
        6..=10 => "intermediate",
        11..=20 => "expert",
        _ => unreachable!(),
    };
    
    println!("{}", stage);
}


// -------------- Compile-time error --------------
thread 'main' panicked at 'internal error: entered unreachable code', src/main.rs:7:20

We can set custom error messages for this as well.

// --- with a custom message ---
_ => unreachable!("Custom message"),
// -------------- Compile-time error --------------
thread 'main' panicked at 'internal error: entered unreachable code: Custom message', src/main.rs:7:20


// --- with debug data ---
_ => unreachable!("level is {}", level),
// -------------- Compile-time error --------------
thread 'main' panicked at 'internal error: entered unreachable code: level is 22', src/main.rs:7:14

assert!(), assert_eq!(), assert_ne!()

These are standard macros which usually use with test assertions.

  • assert!() ensures that a boolean expression is true. It panics if the expression is false.
fn main() {
    let f = false;
    
    assert!(f)
}


// -------------- Compile-time error --------------
thread 'main' panicked at 'assertion failed: f', src/main.rs:4:5
  • assert_eq!() ensures that two expressions are equal. It panics if the expressions are not equal.
fn main() {
    let a = 10;
    let b = 20;
    
    assert_eq!(a, b);
}


// -------------- Compile-time error --------------
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `10`,
 right: `20`', src/main.rs:5:5
  • assert_ne!() ensures that two expressions are not equal. It panics if the expressions are equal.
fn main() {
    let a = 10;
    let b = 10;
    
    assert_ne!(a, b);
}


// -------------- Compile-time error --------------
thread 'main' panicked at 'assertion failed: `(left != right)`
  left: `10`,
 right: `10`', src/main.rs:5:5

⭐ Expressions which use with assert_eq!() and assert_ne!() should return same data type.

We can set custom error messages for these macros as well. For examples,

  1. With a custom message for assert_eq!()
fn main() {
    let a = 10;
    let b = 20;
    
    assert_eq!(a, b, "a and b should be equal");
}


// -------------- Compile-time error --------------
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `10`,
 right: `20`: a and b should be equal', src/main.rs:5:5
  1. assert_eq!() with debug data
fn main() {
    let a = 10;
    let b = 20;

    let c = 40;
    
    assert_eq!(a+b, c, "a = {} ; b = {}", a, b);
}

// -------------- Compile-time error --------------
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `30`,
 right: `40`: a = 10 ; b = 20', src/main.rs:7:5

debug_assert!(), debug_assert_eq!(), debug_assert_ne!()

🔎 These are similar to above assert macros. But these statements are only enabled in non optimized builds by default. All these debug_assert macros will be omitted in release builds, unless we pass -C debug-assertions to the compiler.