Skip to content

Instantly share code, notes, and snippets.

@thomcc
Created July 9, 2021 00:38
Show Gist options
  • Save thomcc/79b607fd12712d64d099f9f32238c647 to your computer and use it in GitHub Desktop.
Save thomcc/79b607fd12712d64d099f9f32238c647 to your computer and use it in GitHub Desktop.
Extracting the message from a `std::thread::Result::Err` (e.g. a `Box<dyn Any + Send>`).
use std::any::Any;
pub fn extract_message(err: &(dyn Any + Send)) -> String {
fn extract(err: &(dyn Any + Send), max_tries: usize) -> String {
if let Some(&s) = err.downcast_ref::<&'static str>() {
return s.into();
}
if let Some(s) = err.downcast_ref::<String>() {
return s.into();
}
if max_tries != 0 {
let wrapped: Option<&Box<dyn Any + Send>> = err.downcast_ref::<Box<dyn Any + Send>>();
if let Some(v) = wrapped {
// This is very subtle! Without `&**` the &Box will itself
// coersce to &dyn Any, which will ofc cause us to recurse
// infinitely. See the caveat in the [official docs][dynanyptr]
// for some discussion of the related issue. (Note: the
// max_tries param exists largely because I got this wrong
// the first time...).
//
// [dynanyptr]: https://doc.rust-lang.org/std/any/index.html#smart-pointers-and-dyn-any
return extract(&**v, max_tries - 1);
}
}
return format!("#<unknown error {:?}>", err.type_id());
}
// Bound recursion to 10. Shouldn't be needed anymore, but you never know...
extract(err, 10)
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_extract_message() {
assert_eq!(extract_message(&boxstr("foo")), "foo");
assert_eq!(extract_message(&boxstring("foo".to_string())), "foo");
assert_eq!(extract_message(&boxbox(boxstr("foo"))), "foo");
}
fn boxstr(s: &'static str) -> Box<dyn Any + Send> {
Box::new(s)
}
fn boxstring(s: String) -> Box<dyn Any + Send> {
Box::new(s)
}
fn boxbox(b: Box<dyn Any + Send>) -> Box<dyn Any + Send> {
Box::new(b)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment