Macro maybe_const_fn

Source
macro_rules! maybe_const_fn {
    ($(
        #[cfg_const($cond:meta)]
        $(#[$attr:meta])*
        $visibility:vis const
        // extra "specifiers" like `async` `unsafe` `extern "C"`
        // needs to be surrounded with {...} due to macro limitations
        //
        // NOTE: Need to use $()* because $()? not supported on 1.31
        $({$($extra_spec:tt)*})*
        fn $name:ident ($($args:tt)*) $( -> $return_tp:ty)* $code:block
    )*) => { ... };
}
Expand description

Mark a function as const if a cfg!(...) attribute evaluates to true.

This has the same effect as using #[rustversion::attr(version_test, const)], but is implemented as a declarative macro instead of a procedural one.

It is subject to some minor limitations (see below).

If these are unacceptable, please use the rustversion procedural macro. That is more robust and supports other markers like unsafe and async.

§Example


maybe_const_fn! {
    #[cfg_const(all())] // always true
    /// Example documentation
    #[inline] // Another attribute
    const fn example() -> u32 {
        3 + 7
    }

    #[cfg_const(any())] // always false
    const fn not_const() -> Vec<u32> {
        vec![3, 7, 8]
    }
}

const FOO: u32 = example();

§Limitations

Please remember to always place const before the fn declaration. Otherwise, the macro will give a value error.

The #[cfg_const(...)] marker must be the first attitude declaration. All other attributes and documentation comments must come after the #[cfg_const(..)] declartion.

The following two examples are broken:


maybe_const_fn! {
    /// doc comment first
    #[cfg_const(all())]
    /// Example documentation
    #[inline] // Another attribute
    const unsafe fn example() -> u32 {
        3 + 7
    }
}

maybe_const_fn! {
    #[inline] // attribute first
    #[cfg_const(all())]
    /// Example documentation
    #[inline] // Another attribute
    const unsafe fn example() -> u32 {
        3 + 7
    }
}

§Additional markers (unsafe, async, etc..)

Additional markers like async, unsafe, and extern "C" must be surrounded by {...} due to macro limitations.

This is correct:


maybe_const_fn! {
    #[cfg_const(all())] // always true
    /// Example documentation
    #[inline] // Another attribute
    const {unsafe} fn example() -> u32 {
        3 + 7
    }
}

const FOO: u32 = unsafe { example() };

This is broken:


maybe_const_fn! {
    #[cfg_const(all())] // always true
    /// Example documentation
    #[inline] // Another attribute
    const unsafe fn example() -> u32 {
        3 + 7
    }
}

const FOO: u32 = unsafe { example() };

§Macro Forwarding

When forwarding a matched fragment inside another macro, the outer macro cannot use fragment specifiers like item for the constant function declaration. As explained in the docs, using the tt and ident fragment specifiers are a special exception.

The following is broken:


macro_rules! item_spec {
    ($bad:item) => {
        maybe_const_fn! {
            #[cfg_const(all())] // always true
            $bad
        }
    }
}

// will give the following error message:
// > captured metavariables except for `:tt`, `:ident` and `:lifetime` cannot be compared to other tokens
item_spec! {
    const fn foo() {}
}