Macro const_panic::impl_panicfmt[][src]

macro_rules! impl_panicfmt {
    ($kind : tt $typename : ident < $($rem : tt) *) => { ... };
    ($kind : tt $typename : ident $($rem : tt) *) => { ... };
}
This is supported on crate feature non_basic only.
Expand description

Implements the PanicFmt trait and the to_panicvals method it requires.

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.

For a derive macro alternative, there’s the PanicFmt derive, which requires the "derive" feature(disabled by default).

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:

example below

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(){
    let foo = Foo {
        x: &[3, 5, 8, 13],
        y: 21,
        z: Bar(false, true),
    };
     
    assert_eq!(
        ArrayString::<100>::from_panicvals(&foo.to_panicvals(FmtArg::DEBUG)).unwrap(),
        "Foo { x: [3, 5, 8, 13], y: 21, z: Bar(false, true) }",
    );
    assert_eq!(
        ArrayString::<200>::from_panicvals(&foo.to_panicvals(FmtArg::ALT_DEBUG)).unwrap(),
        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() {
    let up: Qux<u8> = Qux::Up;
    // Debug formatting the Up variant
    assert_eq!(
        ArrayString::<100>::from_panicvals(&up.to_panicvals(FmtArg::DEBUG)).unwrap(),
        "Up",
    );


    let down: Qux<u16> = Qux::Down { x: 21, y: 34, z: 55 };
    // Debug formatting the Down variant
    assert_eq!(
        ArrayString::<100>::from_panicvals(&down.to_panicvals(FmtArg::DEBUG)).unwrap(),
        "Down { x: 21, y: 34, z: 55 }",
    );
    // Alternate-Debug formatting the Down variant
    assert_eq!(
        ArrayString::<100>::from_panicvals(&down.to_panicvals(FmtArg::ALT_DEBUG)).unwrap(),
        concat!(
            "Down {\n",
            "    x: 21,\n",
            "    y: 34,\n",
            "    z: 55,\n",
            "}",
        )
    );


    let left: Qux<u32> = Qux::Left(89);
    // Debug formatting the Left variant
    assert_eq!(
        ArrayString::<100>::from_panicvals(&left.to_panicvals(FmtArg::DEBUG)).unwrap(),
        "Left(89)",
    );
    // Alternate-Debug formatting the Left variant
    assert_eq!(
        ArrayString::<100>::from_panicvals(&left.to_panicvals(FmtArg::ALT_DEBUG)).unwrap(),
        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;

{
    let with_int = Foo::<&str, u8> {
        value: 100u8,
        _marker: PhantomData,
    };
    assert_eq!(
        ArrayString::<100>::from_panicvals(&with_int.to_panicvals(FmtArg::DEBUG)).unwrap(),
        "Foo { value: 100, _marker: PhantomData }",
    );
}
{
    let with_str = Foo::<bool, &str> {
        value: "hello",
        _marker: PhantomData,
    };
    assert_eq!(
        ArrayString::<100>::from_panicvals(&with_str.to_panicvals(FmtArg::DEBUG)).unwrap(),
        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;

{
    let with_int: Foo<u8, bool, 100> = Foo{
        value: 5,
        _marker: PhantomData,
    };
    assert_eq!(
        ArrayString::<100>::from_panicvals(&with_int.to_panicvals(FmtArg::DEBUG)).unwrap(),
        "Foo { value: 5, _marker: PhantomData }",
    );
}
{
    let with_str: Foo<str, char, 200> = Foo {
        value: 8,
        _marker: PhantomData,
    };
    assert_eq!(
        ArrayString::<100>::from_panicvals(&with_str.to_panicvals(FmtArg::DEBUG)).unwrap(),
        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)>,
    }
}

All the syntax

impl_panicfmt!{
    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]
    )
}