Add name of calling function to `std::panic::Location`

You can currently use #[track_caller] and std::panic::Location to record where a function is called, however Location only exposes the caller's location in source code.

What would be needed to extend this to also expose the calling function's name?

My use case is to provide better error messages when something goes wrong. A native library I'm using only provides a boolean success code which I then turn into an empty CallFailed struct who's message is "The call failed". I'd prefer to have a more useful message like "The call to set_watchdog() failed".

use std::panic::Location;

struct CallFailed {
  caller: &'static Location<'static>,  
}

impl CallFailed {
  #[track_caller]
  pub(crate) fn new() -> Self {
    CallFailed { caller: Location::caller() }
  }
}

impl Display for CallFailed {
  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    write!(f, "The call to `{}()` failed", self.caller.function_name())
  }
}

impl Error for CallFailed {}

I could then create an extension trait for automatically transforming the returned true/false into a Result<(), CallFailed>, again using #[track_caller] to propagate the caller's Location.

pub(crate) trait CallFailedResult {
  fn into_call_failed(self) -> Result<(), CallFailed>;
}

impl CallFailedResult for std::os::raw::c_int {
  #[track_caller]
  fn into_call_failed(self) -> Result<(), CallFailed> {
    if self == 0 {
      Ok(())
    } else {
      Err(CallFailed::new())
    }
  }
}

Without #[track_caller] I'd be forced to use a macro or write out the function name every time CallFailed is created.


@comex made a similar comment in A macro to get the current function name, but it seems that PR was closed. The workaround proposed (stdext::function_name!()) doesn't work with #[track_caller] because I'd need to call the macro directly, not as part of my error type's constructor.

1 Like