Your demo made me realize that this idea has a nasty problem with reference lifetimes/borrow conflicts. 
Using lxrec’s name suggestion for now, and imagining your code being used for the underlying implementation, we would have
fn munge(orig_s: &str) -> String {
show_on_panic!(orig_s);
let mut s = orig_s.to_owned();
show_on_panic!(s);
// ... stuff which modifies s in place ...
s
}
with show_on_panic!(s) expanding to something like
let _gensym_0002 = PanicPrint::new(&s);
and now we have an object live for the entire function, holding an immutable reference to s, which is going to block whatever the rest of the function does to modify s in place.
We could use a raw pointer instead, but that would give up lifetime safety as well as mutability safety, and I’m worried that with sufficiently complicated control flow and/or lifetimes we would wind up maybe trying to print an object that’s already been destroyed. Two-phase borrow and NLL both address related problems but I do not know if they address this case.
Maybe this is addressed sufficiently by what you already did to make the ShowOnPanic struct itself be tied to the lifetime of the object? You had
pub struct PanicPrint<'a, D: Display + ?Sized + 'a> {
msg: &'a D,
}
so the lifetime of the struct is constrained by 'a even if if msg becomes *const D instead of &'a D, right?
(Also, given the probable use cases, I suspect that should be D: Debug but that’s not important right now.)