Skip to main content

context

Macro context 

Source
macro_rules! context {
    () => { ... };
    ($($arg:tt)*) => { ... };
}
Available on crate feature _helpers only.
Expand description

Helper utilities for building procedural macros Creates a closure that generates context strings for error handling with automatic file and line information.

This macro provides a convenient way to add context to errors when using the anyhow crate’s .with_context() method. It automatically prepends the current file name and line number to your context message, making error tracking much easier during debugging.

The macro supports the same syntax as the standard format! macro, allowing for formatted context messages with placeholders and arguments. When no arguments are provided, it creates a simple context with just file and line information.

§Syntax

context!()                          // Just file:line info
context!("message")                 // Static message with file:line
context!("format {}", arg)          // Formatted message with file:line
context!("multiple {} {}", a, b)    // Multiple format arguments
context!("multiple {a} {b}")        // All things that format! supports are supported here too

§Returns

Returns a closure of type impl FnOnce() -> String that can be passed directly to anyhow’s .with_context() method or called manually to get the formatted context string.

§Output Format

The context macro produces strings in the following exact formats:

  • With no message: "src/file.rs:line_number"
    Example: "src/main.rs:42"

  • With message: "src/file.rs:line_number\r\nYour custom message here"
    Example: "src/main.rs:42\r\nOperation failed"

The file path includes the src/ prefix and the line number is automatically determined at compile time using the file! and line! macros. Messages are separated from the location info with a carriage return + line feed (\r\n) sequence.

§Examples

§Basic Usage

use std::fs;

fn risky_operation() -> anyhow::Result<String> {
    // This will show "src/examples.rs:line" if it fails
    fs::read_to_string("missing_file.txt").with_context(context!())
}

let result = risky_operation();
assert!(result.is_err());

let error_msg = format!("{:?}", result.unwrap_err());
// Should contain file path and line
assert!(error_msg.contains(format!("src/examples.rs:{}", line!() - 8).as_str()));

§With Custom Messages

use std::fs;

fn load_config(path: &str) -> anyhow::Result<String> {
    fs::read_to_string(path).with_context(context!("Failed to load config file"))
}

let result = load_config("nonexistent.txt");
assert!(result.is_err());

let error_msg = format!("{:?}", result.unwrap_err());
assert!(
    error_msg.contains(
        format!(
            "src/examples.rs:{}\r\nFailed to load config file",
            line!() - 11
        )
        .as_str()
    )
);

§With Formatted Messages

use std::fs;

fn process_user_data(user_id: u64) -> anyhow::Result<()> {
    let fetch_data = || -> anyhow::Result<String> {
        fs::read_to_string("missing_data.txt")
            .with_context(context!("Failed to fetch data for user {}", user_id))
    };

    let validate_data = |_data: &str| -> anyhow::Result<()> {
        Err(std::io::Error::new(
            std::io::ErrorKind::InvalidData,
            "invalid",
        ))
        .with_context(context!("Data validation failed for user {}", user_id))
    };

    let data = fetch_data()?;
    validate_data(&data)?;

    Ok(())
}

let result = process_user_data(42);
assert!(result.is_err());

let error_msg = format!("{:?}", result.unwrap_err());
assert!(
    error_msg.contains(
        format!(
            "src/examples.rs:{}\r\nFailed to fetch data for user 42",
            line!() - 25
        )
        .as_str()
    )
);

§Chaining Multiple Context Levels

use std::fs;

fn outer_function() -> anyhow::Result<()> {
    inner_function().with_context(context!("Failed in outer function"))
}

fn inner_function() -> anyhow::Result<()> {
    let _ = fs::File::open("nonexistent.txt")
        .with_context(context!("Failed to open configuration file"))?;
    Ok(())
}

let result = outer_function();
assert!(result.is_err());

let error_msg = format!("{:?}", result.unwrap_err());
assert!(
    error_msg.contains(
        format!(
            "src/examples.rs:{}\r\nFailed in outer function",
            line!() - 17
        )
        .as_str()
    )
);
assert!(
    error_msg.contains(
        format!(
            //Spaces are added by anyhow for indentation
            "src/examples.rs:{}\r\n       Failed to open configuration file",
            line!() - 22
        )
        .as_str()
    ),
);

§Manual Context Generation

let ctx = context!("Operation failed with code {}", 500);
let result = ctx();

assert_eq!(
    result,
    format!(
        "src/examples.rs:{}\r\nOperation failed with code 500",
        line!() - 7
    )
);

§See Also

  • anyhow::Context - The trait that provides the .with_context() method
  • format! - The standard formatting macro that this macro’s syntax is based on
  • file! and line! - The macros used internally to get location information