Attributes and Conditional Compilation

#[track_caller]

#[track_caller] is a function and method attribute that passes the caller's source location (&'static std::panic::Location<'static>—containing the file path, line number, and column number of the call site) implicitly into the function. It causes both std::panic::Location::caller() and any panic triggered inside the function to report the caller's line of code rather than the internal line inside the helper function.

Why Option::unwrap, Result::expect, and Index [] Use #[track_caller]

Without #[track_caller], calling opt.unwrap() on None would report a panic at line 950 of library/core/src/option.rs—forcing you to run with RUST_BACKTRACE=1 just to see which line of your own code called .unwrap().

Because Option::unwrap is annotated with #[track_caller], the panic message points directly to the line in your function that called .unwrap()!

Without `#[track_caller]`:
  main() [src/main.rs:20] ---> assert_non_empty("") [src/helpers.rs:5]
                                 `--> Panic reports `src/helpers.rs:5:9`!

With `#[track_caller]` on `assert_non_empty`:
  main() [src/main.rs:20] ---> #[track_caller] assert_non_empty("")
                                 `--> Panic reports `src/main.rs:20:5` (the caller)!

Reading Call-Site Coordinates via Location::caller()

Inside any function (whether annotated with #[track_caller] or not), calling std::panic::Location::caller() returns a &'static Location<'static> struct with .file(), .line(), and .column() methods:

  • In a normal function, Location::caller() returns the location of the Location::caller() call itself.
  • In a #[track_caller] function, Location::caller() returns the location of the external caller—and propagates transitively through chains of #[track_caller] functions!
use std::panic::Location;

#[track_caller]
fn log_check(condition: bool, label: &str) {
    let caller = Location::caller();
    println!(
        "[{}:{}:{}] check {:?} -> {}",
        caller.file(),
        caller.line(),
        caller.column(),
        label,
        if condition { "PASS" } else { "FAIL" }
    );
}

#[track_caller]
fn assert_even(n: i32) {
    // Because both `assert_even` and `log_check` have `#[track_caller]`,
    // `Location::caller()` inside `log_check` points all the way back to `main()`!
    log_check(n % 2 == 0, "is_even");
}

fn main() {
    log_check(2 + 2 == 4, "basic_math");
    assert_even(42);
}
[src/main.rs:22:5] check "basic_math" -> PASS
[src/main.rs:23:5] check "is_even" -> PASS

Rules and Restrictions on #[track_caller]

Rule Detail
Transitive propagation If function A calls #[track_caller] fn B, which calls #[track_caller] fn C, C receives A's call-site location
Trait methods If #[track_caller] is placed on a trait method declaration, it applies across dyn Trait vtable calls as well as static calls
Closures Closures cannot currently be annotated with #[track_caller] on stable Rust; use a named #[track_caller] fn helper instead
FFI incompatibility #[track_caller] can only be used on "Rust" ABI functions (using it on extern "C" fn fails with E0737)