torkleyy
torkleyy

Reputation: 1217

How to embed a Rust macro variable into documentation?

I'd like to use a macro variable in the macro-generated documentation:

macro_rules! impl_foo {
    ($name:ident) => {
        /// Returns a new `$name`.
        fn myfoo() -> $name {

        }
    };
}

However, the variable won't be substituted. I also tried using the #[doc] attribute:

macro_rules! impl_foo {
    ($name:ident) => {
        #[doc = concat!("Returns a new `", $name, "`.")]
        fn myfoo() -> $name {

        }
    };
}

This one even fails to parse: unexpected token: 'concat'

Upvotes: 14

Views: 2550

Answers (2)

Elias Holzmann
Elias Holzmann

Reputation: 3659

While the answer @mcarton gave does work perfectly fine for simple examples, it breaks a bit for more complicated ones. Rustdoc seems to insert spaces between the different doc attributes. The markdown processor strips them out most of the time, but sometimes, it transform them to spaces instead. Consider this example:

macro_rules! impl_foo {
    ($name:ident, $sname:expr) => {
        #[doc = "You can call this as `myfoo("]
        #[doc = $sname]
        #[doc = ")`."]
        pub fn myfoo(_: $name) -> $name {
            42
        }
    };

    ($name:tt) => {
        impl_foo!($name, stringify!($name));
    };
}

impl_foo!(i32);


fn main() {
    println!("Hello, world!");
}

This should generate the documentation "You can call this as myfoo(i32).", but in reality, it results in "You can call this as myfoo( i32 )." (note the additional spaces):Screenshot from the rustdoc page showing the additional spaces

I'm not too sure whether my solution would have worked with the 2017 rustc back when the question was asked, but in modern Rust, this can be done by combining stringify! with concat!:

macro_rules! impl_foo {
    ($name:tt) => {
        #[doc = concat!("You can call this as `myfoo(", stringify!($name), ")`.")]
        pub fn myfoo(_: $name) -> $name {
            42
        }
    };
}

impl_foo!(i32);


fn main() {
    println!("Hello, world!");
}

This results in the documentation you want (so, without superfluous spaces):

Screenshot from the fixed rustdoc page

Upvotes: 10

mcarton
mcarton

Reputation: 30001

This can be done using a recursive macro:

macro_rules! impl_foo {
    ($name:ident, $sname:expr) => {
        #[doc = "Returns a new `"]
        #[doc = $sname]
        #[doc = "`."]
        pub fn myfoo() -> $name {
            42
        }
    };

    ($name:tt) => {
        impl_foo!($name, stringify!($name));
    };
}

impl_foo!(u32);


fn main() {
    println!("Hello, world!");
}

Which renders as:

Example from rustdoc

Upvotes: 20

Related Questions