How do I print a backtrace without panicking using thiserror? - rust

I am running a Rust warp webserver and I need more descriptive error messages. I'd like to print a backtrace or something similar so I can tell where the error started.
I was using the Failure crate, but it is now deprecated so I migrated to thiserror.
Is it possible (without using nightly), to print a backtrace without panicking?

There are ways to get to the backtrace information - but it relies on what are currently "nightly only" APIs. If you're happy to use nightly and stick with thiserror, here's what you do... (If not then see the ASIDE at the end for other ideas).
If you're creating the error from scratch the steps are:
add a backtrace: Backtrace field to your error type.
set the backtrace when you create an error using backtrace::force_captue()
If you're creating this error due to another error and want to use its backtrace instead, then just add #[backtrace] to the source field where you keep the original error.
Then to get the backtrace information you can just use the backtrace() function on std::Error.
An example looks like this:
#![feature(backtrace)]
extern crate thiserror;
use std::backtrace::Backtrace;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataStoreError {
//#[error("data store disconnected")]
//Disconnect(#[from] io::Error),
#[error("the data for key `{0}` is not available")]
Redaction(String),
#[error("invalid header (expected {expected:?}, found {found:?})")]
InvalidHeader {
expected: String,
found: String,
backtrace: Backtrace,
},
#[error("unknown data store error")]
Unknown,
}
pub fn explode() -> Result<(), DataStoreError> {
Err(DataStoreError::InvalidHeader {
expected: "A".to_owned(),
found: "B".to_owned(),
backtrace: Backtrace::force_capture(),
})
}
fn main() {
use std::error::Error;
let e = explode().err().unwrap();
let b = e.backtrace();
println!("e = {}", e);
println!("b = {:#?}", b);
}
This outputs something like:
e = invalid header (expected "A", found "B")
b = Some(
Backtrace [
{ fn: "playground::explode", file: "./src/main.rs", line: 28 },
{ fn: "playground::main", file: "./src/main.rs", line: 34 },
{ fn: "core::ops::function::FnOnce::call_once", file: "/rustc/879aff385a5fe0af78f3d45fd2f0b8762934e41e/library/core/src/ops/function.rs", line: 248 },
...
You can see a working version in the playground
ASIDE
If you're not tied to thiserror and need to use the stable version of the compiler, you could instead use snafu which has support for backtraces using the backtrace crate, rather than std::backtrace, and so works on stable.
There may also be ways to make thiserror work with the backtrace crate rather than std::backtrace but I've not tried that.

Related

Propagate different errors when the function output is fixed by a macro [duplicate]

I want to learn how to properly deal with errors in Rust. I have read the book and this example; now I would like to know how I should deal with errors in this function:
fn get_synch_point(&self) -> Result<pv::synch::MeasPeriods, reqwest::Error> {
let url = self.root.join("/term/pv/synch"); // self.root is url::Url
let url = match url {
Ok(url) => url,
// ** this err here is url::ParseError and can be converted to Error::Kind https://docs.rs/reqwest/0.8.3/src/reqwest/error.rs.html#54-57 **//
Err(err) => {
return Err(Error {
kind: ::std::convert::From::from(err),
url: url.ok(),
})
}
};
Ok(reqwest::get(url)?.json()?) //this return reqwest::Error or convert to pv::sych::MeasPeriods automaticly
}
This code is improper; it causes a compilation error:
error[E0451]: field `kind` of struct `reqwest::Error` is private
--> src/main.rs:34:42
|
34 | Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `kind` is private
error[E0451]: field `url` of struct `reqwest::Error` is private
--> src/main.rs:34:81
|
34 | Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})
| ^^^^^^^^^^^^^ field `url` is private
What is a proper pattern to deal with that case? For me, reqwest::Error in this case is a good solution so I would like to avoid defining my own error type:
enum MyError {
Request(reqwest::Error),
Url(url::ParseError) // this already a part of request::Error::Kind!!!
}
Update 2020
The rust programming language is evolving quickly so a new answer can be added! I really liked custom_error but now I think thiserror will be my loved one!
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataStoreError {
#[error("data store disconnected")]
Disconnect(#[from] io::Error),
#[error("the data for key `{0}` is not available")]
Redaction(String),
#[error("invalid header (expected {expected:?}, found {found:?})")]
InvalidHeader {
expected: String,
found: String,
},
#[error("unknown data store error")]
Unknown,
}
This allow change io::Error to DataStoreError::Disconnect with question mark ?. Go here for details
useful links:
great blog about using thiserror in combine with anyhow
Other interesting crates:
anyhow - Flexible concrete Error type built on std::error::Error
snafu - Situation Normal: All Fouled Up - SNAFU is a library to easily assign underlying errors into domain-specific errors while adding context. (similar to thiserror)
custom_error - This crate contains a macro that should make it easier to define custom errors without having to write a lot of boilerplate code.
for panics:
proc-macro-error - This crate aims to make error reporting in proc-macros simple and easy to use.
human-panic - Panic messages for humans. Handles panics by calling std::panic::set_hook to make errors nice for humans.
Unfortunately, in your case you cannot create a reqwest::Error from other error types, if the reqwest library does not provide a way to do so (and it likely doesn't). To solve this problem, which is very common, especially in applications which use multiple libraries, the proper solution would be one of the following:
Declare your own custom enum with all errors your application works with (or one subsystem of your application; granularity highly depends on the project), and declare From conversions from all errors you work with to this enum type.
As an extension of this approach, you can use error-chain (or quick-error, on which error-chain is basically based) to generate such custom types and conversions in a semi-automatic way.
Use a special, generic error type. There are basically two of them:
a. Box<Error> where Error is defined in the standard library.
b. Use the Error type defined in the failure crate.
Then the question mark operator will be able to convert any compatible error to one of these types because of various Into and From trait implementations.
Note that the failure crate is intended to be the way to define errors promoted in the Rust community. Not only does it provide a common error type and trait (which fixes various issues with the std::error::Error trait; see for example here), it also has facilities to define your own error types (for example, with failure_derive), and for tracking error context, causes and generating backtrace. Additionally, it tries to be as compatible with the existing error handling approaches as possible, therefore it can be used to integrate with libraries which use other, older approaches (std::error::Error, error-chain, quick-error) quite easily. So I strongly suggest you to consider using this crate first, before other options.
I have already started using failure in my application projects, and I just can't express how much easier and nicer error handling has become. My approach is as follows:
Define the Result type:
type Result<T> = std::result::Result<T, failure::Error>;
Use Result<Something> everywhere where an error can be returned, using the question mark operator (?) to convert between errors and functions like err_msg or format_err! or bail! to create my own error messages.
I have yet to write a library using failure, but I imagine that for libraries it would be important to create more specific errors declared as an enum, which can be done with the failure_derive crate. For applications, though, the failure::Error type is more than enough.
In that case, reusing the underlying error type is not possible because you cannot construct its hidden fields. And even when it is possible, I would advise against it, in order to make your code more flexible and future-proof.
Defining custom error types can involve writing a lot of boilerplate, but fortunately several libraries exist to alleviate this pain. failure, error-chain and quick-error were already mentioned above, but I would like to point you to a crate I wrote that involves even less boilerplate than the others : custom_error. With it, you can write:
#[macro_use] extern crate custom_error;
custom_error!{ MyError
Request{source: reqwest::Error} = "request error",
Url{source: url::ParseError} = "invalid url"
}
As already stated by
Vladimir Matveev, the failure crate should be your starting point. Here is my solution:
use std::io;
use std::result;
use failure::{Backtrace, Fail};
/// This is a new error type manged by Oxide library.
/// The custom derive for Fail derives an impl of both Fail and Display.
#[derive(Debug, Fail)]
pub enum OxideError {
#[fail(display = "{}", message)]
GeneralError { message: String },
#[fail(display = "{}", message)]
IoError {
message: String,
backtrace: Backtrace,
#[cause]
cause: io::Error,
},
}
/// Create general error
pub fn general(fault: &str) -> OxideError {
OxideError::GeneralError {
message: String::from(fault),
}
}
/// Create I/O error with cause and backtrace
pub fn io(fault: &str, error: io::Error) -> OxideError {
OxideError::IoError {
message: String::from(fault),
backtrace: Backtrace::new(),
cause: error,
}
}
This error enumeration is extendible which allows it to accommodate future modifications that might be made to the program.
It seems the anyhow crate is good excepts for not supporting Box<dyn StdError>.
Most error types implemented Display trait, so use this magic.
Customize an error
Implement From trait from all object that implements Display
pub struct MyError {
pub code: i32,
pub message: String,
}
impl<E: Display> From<E> for MyError {
fn from(value: E) -> Self {
MyError {
code: 0,
message: value.to_string(),
}
}
}
async fn hello() -> std::result::Result<(), &'static str> {
return Err("Hello world");
}
async fn test() -> std::result::Result<(), MyError> {
// &str
hello().await?;
// std::io::Error
std::fs::read("DD")?;
// serde_json::Error
let data = serde_json::from_str("[1,2,3]")?;
Ok(())
}

How do I get the return address of a function?

I am writing a Rust library containing an implementation of the callbacks for LLVM SanitizerCoverage. These callbacks can be used to trace the execution of an instrumented program.
A common way to produce a trace is to print the address of each executed basic block. However, in order to do that, it is necessary to retrieve the address of the call instruction that invoked the callback. The C++ examples provided by LLVM rely on the compiler intrinsic __builtin_return_address(0) in order to obtain this information.
extern "C" void __sanitizer_cov_trace_pc_guard(uint32_t *guard) {
if (!*guard) return;
void *PC = __builtin_return_address(0);
printf("guard: %p %x PC %p\n", guard, *guard, PC);
}
I am trying to reproduce the same function in Rust but, apparently, there is no equivalent to __builtin_return_address. The only reference I found is from an old version of Rust, but the function described is not available anymore. The function is the following:
pub unsafe extern "rust-intrinsic" fn return_address() -> *const u8
My current hacky solution involves having a C file in my crate that contains the following function:
void* get_return_address() {
return __builtin_return_address(1);
}
If I call it from a Rust function, I am able to obtain the return address of the Rust function itself. This solution, however, requires the compilation of my Rust code with -C force-frame-pointers=yes for it to work, since the C compiler intrinsic relies on the presence of frame pointers.
Concluding, is there a more straightforward way of getting the return address of the current function in Rust?
edit: The removal of the return_address intrinsic is discussed in this GitHub issue.
edit 2: Further testing showed that the backtrace crate is able to correctly extract the return address of the current function, thus avoiding the hack I described before. Credit goes to this tweet.
The problem with this solution is the overhead that is generated creating a full backtrace when only the return address of the current function is needed. In addition, the crate is using C libraries to extract the backtrace; this looks like something that should be done in pure Rust.
edit 3: The compiler intrinsic __builtin_return_address(0) generates a call to the LLVM intrinsic llvm.returnaddress. The corresponding documentation can be found here.
I could not find any official documentation about this, but found out by asking in the rust-lang repository: You can link against LLVM intrinsics, like llvm.returnaddress, with only a few lines of code:
extern {
#[link_name = "llvm.returnaddress"]
fn return_address() -> *const u8;
}
fn foo() {
println!("I was called by {:X}", return_address());
}
The LLVM intrinsic llvm.addressofreturnaddress might also be interesting.
As of 2022 Maurice's answer doesn't work as-is and requires an additional argument.
#![feature(link_llvm_intrinsics)]
extern {
#[link_name = "llvm.returnaddress"]
fn return_address(a: i32) -> *const u8;
}
macro_rules! caller_address {
() => {
unsafe { return_address(0) }
};
}
fn print_caller() {
println!("caller: {:p}", caller_address!());
}
fn main() {
println!("main address: {:p}", main as *const ());
print_caller();
}
Output:
main address: 0x56261a13bb50
caller: 0x56261a13bbaf
Playground link;

Rust Book 12.3 - unresolved import error [E4032]

I am new to Rust and working on the exercise in Chapter 12.3 in the book.
I am pretty confident that my code is the same as that in the book (hard to tell for sure because of the 'snips'). However I get an unresolved import error when I try cargo build or cargo run from the project directory, minigrep/
src/main.rs
use std::env;
use std::process;
use minigrep;
use minigrep::Config;
fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::new(&args).unwrap_or_else(|err| {
println!("Problem parsing args: {}", err);
process::exit(1);
});
println!("Searching for {}", config.query);
println!("In file {}", config.filename);
if let Err(e) = minigrep::run(config) {
println!("Application error: {}", e);
process::exit(1);
}
}
src/lib.rs
use std::fs;
use std::error::Error;
pub struct Config {
pub query: String,
pub filename: String,
}
impl Config {
pub fn new(args: &[String]) -> Result <Config, &'static str> {
if args.len() < 3 {
return Err("not enough args");
}
let query = args[1].clone();
let filename = args[2].clone();
Ok(Config { query, filename })
}
}
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(config.filename)?;
println!("With text:\n {}", contents);
Ok(())
}
There is a contradiction between the code samples for the exercise in chapter 12.3 and the earlier section in chapter 7: '7.2 -- Modules and use to control scope -- Separating modules into different files'.
Using 7.2's syntax complies:
mod lib; //.. instead of 'use minigrep'
use lib::Config; //.. instead of 'use minigrep::Config'
Neither of the above answers is correct. Using cargo clean is the correct solution. The incremental build process that produces that red underline in the IDE is a result of cached information about the program and library.
$ cargo clean
This seems to be a VSCode issue. Simply restarting VSCode makes the error go away, as discussed on this GitHub issue: https://github.com/rust-lang/vscode-rust/issues/686
I had this issue because I accidentally chose the wrong file name for the lib.rs. Make sure the file is named lib.rs without a typo. Other file names do not work here because of the use statement.
When you write use minigrep::Config, the compiler will look for a lib.rs file in the current project minigrep to resolve the imports.
I just ran across the same problem when reviewing The Rust Programming Language. The way I got it to compile was to remove the line use minigrep; from src/main.rs and add extern crate minigrep to the top of the file. The other file can stay as it is. Not sure if this a typo in the book (if it is then it is in multiple versions) or if there is a change to the behavior of the Rust compiler to accommodate the new syntax and we are just using a version that is too old. For comparison my version is 1.31.0.

Is there a way to make expect() output a more user-friendly message?

I find the message produced by expect() to be very unfriendly for users. Consider the following short example...
use std::env;
fn main() {
let imagefn = env::args().skip(1).next().expect("Filename not provided.");
println!("{}", imagefn);
}
That errors with:
thread 'main' panicked at 'Filename not provided.', libcore/option.rs:960:5
note: Run with `RUST_BACKTRACE=1` for a backtrace.
I find expect() very useful for writing quick code, but wish I could output something more like this:
Filename not provided.
With all the other information hidden unless I actually provide the environment variable, which I, as a developer, should know about. I guess my questions are:
Is there a way I can override expect() to do this?
Why does expect() output its unfriendly message even in release builds?
You can use set_hook to change the panic message. Example:
use std::panic::set_hook;
fn main() {
set_hook(Box::new(|info| {
if let Some(s) = info.payload().downcast_ref::<String>() {
println!("{}", s);
}
}));
// Displays: "My error message":
Option::None::<i32>.expect("My error message");
}
You can also use message() that is simpler, but unstable (for now):
#![feature(panic_info_message)]
use std::panic::set_hook;
fn main() {
set_hook(Box::new(|info| {
println!("{:?}", info.message().unwrap());
}));
Option::None::<i32>.expect("My error message");
}
Note that you can create your own extension method that panics with a custom type. In the panic hook, if you can downcast to your custom type, you are certain of the origin of the panic.
expect() is just a convenient conditional call to panic!():
pub fn expect(self, msg: &str) -> T {
match self {
Some(val) => val,
None => expect_failed(msg) // expect_failed calls panic!()
}
}
Ideally you should probably handle this with the ? operator inside a function returning an Option or Result in order to be able to handle this sort of issue in a more graceful manner.
If you would just like to return a more friendly-looking message and quit, you could implement your own function printing the message and terminating with process::exit.

Rust proper error handling (auto convert from one error type to another with question mark)

I want to learn how to properly deal with errors in Rust. I have read the book and this example; now I would like to know how I should deal with errors in this function:
fn get_synch_point(&self) -> Result<pv::synch::MeasPeriods, reqwest::Error> {
let url = self.root.join("/term/pv/synch"); // self.root is url::Url
let url = match url {
Ok(url) => url,
// ** this err here is url::ParseError and can be converted to Error::Kind https://docs.rs/reqwest/0.8.3/src/reqwest/error.rs.html#54-57 **//
Err(err) => {
return Err(Error {
kind: ::std::convert::From::from(err),
url: url.ok(),
})
}
};
Ok(reqwest::get(url)?.json()?) //this return reqwest::Error or convert to pv::sych::MeasPeriods automaticly
}
This code is improper; it causes a compilation error:
error[E0451]: field `kind` of struct `reqwest::Error` is private
--> src/main.rs:34:42
|
34 | Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ field `kind` is private
error[E0451]: field `url` of struct `reqwest::Error` is private
--> src/main.rs:34:81
|
34 | Err(err) => return Err(Error{kind: ::std::convert::From::from(err), url: url.ok()})
| ^^^^^^^^^^^^^ field `url` is private
What is a proper pattern to deal with that case? For me, reqwest::Error in this case is a good solution so I would like to avoid defining my own error type:
enum MyError {
Request(reqwest::Error),
Url(url::ParseError) // this already a part of request::Error::Kind!!!
}
Update 2020
The rust programming language is evolving quickly so a new answer can be added! I really liked custom_error but now I think thiserror will be my loved one!
use thiserror::Error;
#[derive(Error, Debug)]
pub enum DataStoreError {
#[error("data store disconnected")]
Disconnect(#[from] io::Error),
#[error("the data for key `{0}` is not available")]
Redaction(String),
#[error("invalid header (expected {expected:?}, found {found:?})")]
InvalidHeader {
expected: String,
found: String,
},
#[error("unknown data store error")]
Unknown,
}
This allow change io::Error to DataStoreError::Disconnect with question mark ?. Go here for details
useful links:
great blog about using thiserror in combine with anyhow
Other interesting crates:
anyhow - Flexible concrete Error type built on std::error::Error
snafu - Situation Normal: All Fouled Up - SNAFU is a library to easily assign underlying errors into domain-specific errors while adding context. (similar to thiserror)
custom_error - This crate contains a macro that should make it easier to define custom errors without having to write a lot of boilerplate code.
for panics:
proc-macro-error - This crate aims to make error reporting in proc-macros simple and easy to use.
human-panic - Panic messages for humans. Handles panics by calling std::panic::set_hook to make errors nice for humans.
Unfortunately, in your case you cannot create a reqwest::Error from other error types, if the reqwest library does not provide a way to do so (and it likely doesn't). To solve this problem, which is very common, especially in applications which use multiple libraries, the proper solution would be one of the following:
Declare your own custom enum with all errors your application works with (or one subsystem of your application; granularity highly depends on the project), and declare From conversions from all errors you work with to this enum type.
As an extension of this approach, you can use error-chain (or quick-error, on which error-chain is basically based) to generate such custom types and conversions in a semi-automatic way.
Use a special, generic error type. There are basically two of them:
a. Box<Error> where Error is defined in the standard library.
b. Use the Error type defined in the failure crate.
Then the question mark operator will be able to convert any compatible error to one of these types because of various Into and From trait implementations.
Note that the failure crate is intended to be the way to define errors promoted in the Rust community. Not only does it provide a common error type and trait (which fixes various issues with the std::error::Error trait; see for example here), it also has facilities to define your own error types (for example, with failure_derive), and for tracking error context, causes and generating backtrace. Additionally, it tries to be as compatible with the existing error handling approaches as possible, therefore it can be used to integrate with libraries which use other, older approaches (std::error::Error, error-chain, quick-error) quite easily. So I strongly suggest you to consider using this crate first, before other options.
I have already started using failure in my application projects, and I just can't express how much easier and nicer error handling has become. My approach is as follows:
Define the Result type:
type Result<T> = std::result::Result<T, failure::Error>;
Use Result<Something> everywhere where an error can be returned, using the question mark operator (?) to convert between errors and functions like err_msg or format_err! or bail! to create my own error messages.
I have yet to write a library using failure, but I imagine that for libraries it would be important to create more specific errors declared as an enum, which can be done with the failure_derive crate. For applications, though, the failure::Error type is more than enough.
In that case, reusing the underlying error type is not possible because you cannot construct its hidden fields. And even when it is possible, I would advise against it, in order to make your code more flexible and future-proof.
Defining custom error types can involve writing a lot of boilerplate, but fortunately several libraries exist to alleviate this pain. failure, error-chain and quick-error were already mentioned above, but I would like to point you to a crate I wrote that involves even less boilerplate than the others : custom_error. With it, you can write:
#[macro_use] extern crate custom_error;
custom_error!{ MyError
Request{source: reqwest::Error} = "request error",
Url{source: url::ParseError} = "invalid url"
}
As already stated by
Vladimir Matveev, the failure crate should be your starting point. Here is my solution:
use std::io;
use std::result;
use failure::{Backtrace, Fail};
/// This is a new error type manged by Oxide library.
/// The custom derive for Fail derives an impl of both Fail and Display.
#[derive(Debug, Fail)]
pub enum OxideError {
#[fail(display = "{}", message)]
GeneralError { message: String },
#[fail(display = "{}", message)]
IoError {
message: String,
backtrace: Backtrace,
#[cause]
cause: io::Error,
},
}
/// Create general error
pub fn general(fault: &str) -> OxideError {
OxideError::GeneralError {
message: String::from(fault),
}
}
/// Create I/O error with cause and backtrace
pub fn io(fault: &str, error: io::Error) -> OxideError {
OxideError::IoError {
message: String::from(fault),
backtrace: Backtrace::new(),
cause: error,
}
}
This error enumeration is extendible which allows it to accommodate future modifications that might be made to the program.
It seems the anyhow crate is good excepts for not supporting Box<dyn StdError>.
Most error types implemented Display trait, so use this magic.
Customize an error
Implement From trait from all object that implements Display
pub struct MyError {
pub code: i32,
pub message: String,
}
impl<E: Display> From<E> for MyError {
fn from(value: E) -> Self {
MyError {
code: 0,
message: value.to_string(),
}
}
}
async fn hello() -> std::result::Result<(), &'static str> {
return Err("Hello world");
}
async fn test() -> std::result::Result<(), MyError> {
// &str
hello().await?;
// std::io::Error
std::fs::read("DD")?;
// serde_json::Error
let data = serde_json::from_str("[1,2,3]")?;
Ok(())
}

Resources