amanjeev sethi

science, infrastructure, code, photography

How to document Rust's macro invocation

written by amanjeev on 2019-02-21

Since, I have spent way too much time on this, I decided it is worth noting down.

The problem

You can easily document your Rust items like functions by putting three slashes ///.

/// My amazing function
/// # Example
/// amazing();
fn amazing() -> () {
    println!("Rust is amazing!");
}

You can also document your macro like this -

/// My amazing macro's own doc
macro_rules! amazing {
    ($func:ident) => (
        fn $func() -> () {
            println!("Rust is amazing! Rust has powerful features!");
        };   
    )
}

However, if you want to document each separate invocation of your amazing! macro, it is not that straightfoward. You might ask why should we be doing that anyway? Because macros can generate a lot of boilerplate code for you and each of those invocations might be slightly different which must be conveyed to your user in docs.rs. For example, this will not work

/// this is a macro invocation but
/// these doc strings will not show
amazing!(amazing_function_1);

Phew!

Solution

Slightly modify your macro

/// My amazing macro's own doc 
macro_rules! amazing {
    (
        $(#[$meta:meta])*
        $func:ident
    ) => (
        $(#[meta])*
        fn $func() -> () {
            println!("Rust is amazing! Rust has powerful features!");
        };
    )
}

Let me explain briefly what those two new lines mean.

Capture meta

$(#[$meta:meta])* tells the macro to match 0 or more instances of meta attributes. Rust doc comment /// a comment is just another way of saying #[doc = " a comment"] This means that if we provide a comment in our invocation, this will now be captured.

Output meta

$(#[meta])* just spits out all the meta attributes right before the function.

Add comment in your invocation

amazing!(
    /// this macro invocation will have a comment
    /// # Examples
    /// amazing_function_2();
    amazing_function_2
)

amazing!(
    /// this macro invocation will have another, its own, comment
    /// # Examples
    /// amazing_function_3();
    amazing_function_3
)

The comments in the docs will now show up for both functions that were created via invoking macro. :)

Many thanks to kind folks in Rust community discord server.