macro_rules! impl_panicfmt {
(
$(# $attrs:tt)*
$kind:ident $typename:ident < $($rem:tt)*
) => { ... };
(
$(# $attrs:tt)*
$kind:ident $typename:ident $($rem:tt)*
) => { ... };
}Expand description
Implements the PanicFmt
trait and the to_panicvals method it requires.
For a derive macro alternative, there’s the PanicFmt derive,
which requires the "derive" feature(disabled by default).
§Syntax
This macro roughly takes a type definition and a (conditionally required) list of impls, for an example demonstrating all the parts of the syntax look here.
This macro has these optional attributes that go above the item definition (and must go in this order):
-
#[pfmt(display_fmt = $display_fmt:expr)](example below): Tells the macro to use the$display_fmtfunction to Display-format the type. -
#[pfmt(panicvals_lower_bound = $panicvals_lower_bound:expr)]: Tells the macro to use at least$panicvals_lower_boundPanicVals for formatting the type, useful for Display formatting with the#[pfmt(display_fmt = ...)]attribute.
§Limitations
§Type parameters
Types with type parameters can’t be generically formatted, which has two workarounds.
The first workaround is marking a type parameter as ignored with an ignore prefix,
if the type parameter(s) are only used in marker types (eg: PhantomData).
example of this workaround
The second workaround is to implement panic formatting with concrete type arguments,
using trailing (impl Foo<Bar>)s.
example of this workaround
This limitation is caused by:
- the lack of trait bound support in stable const fns.
- the need to have a concrete type argument
§Const parameters
Const parameters must not affect the value of the PanicFmt::PV_COUNT of this type,
since the const parameter must be replaceable with a concrete value.
Note that arrays have a PV_COUNT of 1 for all lengths.
§Concrete Self type for PanicFmt::PV_COUNT
The to_panicvals method that this macro generates roughly returns a
[PanicVal<'_>; <Self as PanicFmt>::PV_COUNT]Because of limitations in stable const generics,
the generic arguments of Self in the above code must be replaced with concrete arguments,
requiring:
- Lifetime arguments to be replaced with
'_ - Type arguments to be replaced with concrete types (usually
()) - Const arguments to be replaced with concrete values (usually the default value for the type)
§Examples
§Struct formatting
use const_panic::{ArrayString, FmtArg, impl_panicfmt};
fn main(){
const FOO: Foo = Foo {
x: &[3, 5, 8, 13],
y: 21,
z: Bar(false, true),
};
assert_eq!(
const_panic::concat_!(FmtArg::DEBUG; FOO),
"Foo { x: [3, 5, 8, 13], y: 21, z: Bar(false, true) }",
);
assert_eq!(
const_panic::concat_!(FmtArg::ALT_DEBUG; FOO),
concat!(
"Foo {\n",
" x: [\n",
" 3,\n",
" 5,\n",
" 8,\n",
" 13,\n",
" ],\n",
" y: 21,\n",
" z: Bar(\n",
" false,\n",
" true,\n",
" ),\n",
"}",
),
);
}
struct Foo<'a> {
x: &'a [u8],
y: u8,
z: Bar,
}
// Implementing `PanicFmt` and the `to_panicvals` method for `Foo<'a>`
impl_panicfmt!{
struct Foo<'a> {
x: &'a [u8],
y: u8,
z: Bar,
}
}
struct Bar(bool, bool);
impl_panicfmt!{
struct Bar(bool, bool);
}
§Enum Formatting
use const_panic::{ArrayString, FmtArg, impl_panicfmt};
fn main() {
const UP: Qux<u8> = Qux::Up;
// Debug formatting the Up variant
assert_eq!(
const_panic::concat_!(FmtArg::DEBUG; UP),
"Up",
);
const DOWN: Qux<u16> = Qux::Down { x: 21, y: 34, z: 55 };
// Debug formatting the Down variant
assert_eq!(
const_panic::concat_!(FmtArg::DEBUG; DOWN),
"Down { x: 21, y: 34, z: 55 }",
);
// Alternate-Debug formatting the Down variant
assert_eq!(
const_panic::concat_!(FmtArg::ALT_DEBUG; DOWN),
concat!(
"Down {\n",
" x: 21,\n",
" y: 34,\n",
" z: 55,\n",
"}",
)
);
const LEFT: Qux<u32> = Qux::Left(89);
// Debug formatting the Left variant
assert_eq!(
const_panic::concat_!(FmtArg::DEBUG; LEFT),
"Left(89)",
);
// Alternate-Debug formatting the Left variant
assert_eq!(
const_panic::concat_!(FmtArg::ALT_DEBUG; LEFT),
concat!(
"Left(\n",
" 89,\n",
")",
)
);
}
enum Qux<T> {
Up,
Down { x: T, y: T, z: T },
Left(u64),
}
// Because of limitations of stable const evaluation,
// `Qux` can't generically implement panic formatting,
// so this macro invocation implements panic formatting for these specifically:
// - `Qux<u8>`
// - `Qux<u16>`
// - `Qux<u32>`
impl_panicfmt!{
enum Qux<T> {
Up,
Down { x: T, y: T, z: T },
Left(u64),
}
(impl Qux<u8>)
(impl Qux<u16>)
(impl Qux<u32>)
}§Type parameters
This example demonstrates support for types with type parameters.
use const_panic::{ArrayString, FmtArg, impl_panicfmt};
use std::marker::PhantomData;
{
const WITH_INT: Foo<&str, u8> = Foo {
value: 100u8,
_marker: PhantomData,
};
assert_eq!(
const_panic::concat_!(FmtArg::DEBUG; WITH_INT),
"Foo { value: 100, _marker: PhantomData }",
);
}
{
const WITH_STR: Foo<bool, &str> = Foo {
value: "hello",
_marker: PhantomData,
};
assert_eq!(
const_panic::concat_!(FmtArg::DEBUG; WITH_STR),
r#"Foo { value: "hello", _marker: PhantomData }"#,
);
}
#[derive(Debug)]
pub struct Foo<A, B> {
value: B,
_marker: PhantomData<A>,
}
impl_panicfmt!{
// `ignore` here tells the macro that this type parameter is not formatted.
struct Foo<ignore A, B> {
value: B,
_marker: PhantomData<A>,
}
// Because type parameters can't be generically formatted,
// you need to list impls with concrete `B` type arguments.
//
// the generic parameters to the impl block go inside `[]`
(impl[A] Foo<A, u8>)
// the bounds in where clauses also go inside `[]`
(impl[A] Foo<A, &str> where[A: 'static])
}
§Phantom Type parameters
This example demonstrates how type parameters can be ignored.
use const_panic::{ArrayString, FmtArg, impl_panicfmt};
use std::marker::PhantomData;
{
const WITH_INT: Foo<u8, bool, 100> = Foo{
value: 5,
_marker: PhantomData,
};
assert_eq!(
const_panic::concat_!(FmtArg::DEBUG; WITH_INT),
"Foo { value: 5, _marker: PhantomData }",
);
}
{
const WITH_STR: Foo<str, char, 200> = Foo {
value: 8,
_marker: PhantomData,
};
assert_eq!(
const_panic::concat_!(FmtArg::DEBUG; WITH_STR),
r#"Foo { value: 8, _marker: PhantomData }"#,
);
}
#[derive(Debug)]
pub struct Foo<A: ?Sized, B, const X: u32> {
value: u32,
_marker: PhantomData<(PhantomData<A>, B)>,
}
impl_panicfmt!{
// `ignore` here tells the macro that this type parameter is not formatted.
//
// `ignore(u8)` tells the macro to use `u8` as the `B` type parameter for
// `<Foo<....> as PanicFmt>::PV_COUNT` in the generated `to_panicvals` method.
struct Foo<ignore A, ignore(u8) B, const X: u32>
// bounds must be written in the where clause, like this:
where[ A: ?Sized ]
{
value: u32,
_marker: PhantomData<(PhantomData<A>, B)>,
}
}§Display formatting
use const_panic::{impl_panicfmt, FmtArg, PanicFmt, PanicVal};
assert_eq!(const_panic::concat_!(debug: Foo([3, 5, 8])), "Foo([3, 5, 8])");
assert_eq!(const_panic::concat_!(display: Foo([3, 5, 8])), "3 5 8");
struct Foo([u8; 3]);
impl_panicfmt! {
// these (optional) attributes are the only supported struct-level attributes and
// can only go in this order
#[pfmt(display_fmt = Self::display_fmt)]
// need this attribute to output more PanicVals in Display formatting than
// in Debug formatting.
#[pfmt(panicvals_lower_bound = 10)]
struct Foo([u8; 3]);
}
impl Foo {
const fn display_fmt(&self, fmtarg: FmtArg) -> [PanicVal<'_>; Foo::PV_COUNT] {
let [a, b, c] = self.0;
const_panic::flatten_panicvals!{fmtarg, Foo::PV_COUNT;
a, " ", b, " ", c
}
}
}
§All the syntax
impl_panicfmt!{
// these are the only supported struct-level attributes and can only go in this order
#[pfmt(display_fmt = Self::display_fmt)]
#[pfmt(panicvals_lower_bound = 100)]
struct Foo<
'a,
'b,
// For type parameters that aren't formatted.
// Removes the `PanicFmt` bound on this type parameter
// and uses `()` as the type argument for this to get
// `<Foo<....> as PanicFmt>::PV_COUNT` in the generated `to_panicvals` methods.
ignore C,
// same as `C`, using `u8` instead of `()`
ignore(u8) D,
// un-`ignore`d type parameters must be replaced with concrete types in the impls below
E,
const X: u32,
>
// bounds must be written in the where clause, like this:
where[ C: ?Sized, D: ?Sized, E: ?Sized ]
{
_lifetimes: PhantomData<(&'a (), &'b ())>,
_marker: PhantomData<(PhantomData<C>, PhantomData<D>, PhantomData<E>)>,
}
// The impls for this type, this is required for types with un-`ignore`d type parameters.
// Otherwise, a generic impl is automatically generated.
(
impl['a, 'b, D, E: ?Sized, const X: u32] Foo<'a, 'b, D, E, u32, X>
where[D: ?Sized]
)
(
impl['a, 'b, D, E: ?Sized, const X: u32] Foo<'a, 'b, D, E, &str, X>
where[D: ?Sized]
)
}
impl<'a, 'b, C, D, E, const X: u32> Foo<'a, 'b, C, D, E, X>
where
C: ?Sized,
D: ?Sized,
E: ?Sized,
{
const fn display_fmt(
&self,
fmt: FmtArg,
) -> [PanicVal<'_>; <Foo<'_, '_, (), u8, u32, 0>>::PV_COUNT] {
const_panic::flatten_panicvals!{fmt, <Foo<'_, '_, (), u8, u32, 0>>::PV_COUNT;
"Foo: ", X
}
}
}