Is there any way to inline a const inside a doc comment (rendered by cargo doc)?

1k views Asked by At

With "default" constructors, it can be useful to document what the… defaults are. If this is textually defined in the doc and separately defined as a literal or a static / const, the two can get out of sync:

impl Foo {
    /// Creates a [Foo] with a `bar` of 3.
    fn new() -> Foo { Foo::new_with_bar(5) }
    /// Creates a [Foo] with the provided `bar`.
    fn new_with_bar(bar: usize) -> Foo { Foo { bar } }
}

It's possible to extract the literal to a const or static and link to that, but then the reader has to go through the indirection to know what the value is, and the const / static has to be pub or cargo doc complains and refuses to link to it.

Is there any way to substitute the const value in the docstring instead of linking to it? Or some other method which would avoid the indirection? aka

const DEFAULT_BAR: usize = 5
impl Foo {
    /// Creates a [Foo] with a `bar` of ???DEFAULT_BAR???.
    fn new() -> Foo { Foo::new_with_bar(DEFAULT_BAR) }
}

should be rendered as:

pub fn new() -> Foo

Creates a Foo with a bar of 5.

Although similar, How to embed a Rust macro variable into documentation? doesn't seem to apply here. [doc] complains about an unexpected token when given a name as a parameter (even a const str) and I don't know if a wrapping macro can force the substitution of a const.

2

There are 2 answers

1
Shepmaster On BEST ANSWER

This works in Rust 1.47:

struct Foo { bar: usize }

macro_rules! impl_foo {
    ($bar_def:expr) => { impl_foo!(@ $bar_def, stringify!($bar_def)); };
    (@ $bar_def:expr, $bar_def_str:expr) => {
        impl Foo {
            /// Creates a [Foo] with a `bar` of
            #[doc = $bar_def_str]
            ///.
            fn new() -> Foo { Foo::new_with_bar($bar_def) }

            /// Creates a [Foo] with the provided `bar`.
            fn new_with_bar(bar: usize) -> Foo { Foo { bar } }
        }
    }
}

impl_foo!(3);

doc output

You can use the paste to avoid the redirection:

use paste::paste;

struct Foo { bar: usize }

macro_rules! impl_foo {
    ($bar_def:expr) => {
        paste! {
            impl Foo {
                #[doc = "Creates a [Foo] with a `bar` of " $bar_def "."]
                fn new() -> Foo { Foo::new_with_bar($bar_def) }

                /// Creates a [Foo] with the provided `bar`.
                fn new_with_bar(bar: usize) -> Foo { Foo { bar } }
            }
        }
    }
}

impl_foo!(3);

In the future, you'll be able to skip the re-direction in the macro (or the usage of paste!) by using #![feature(extended_key_value_attributes)], as described in vallentin's answer.

See also:

2
vallentin On

On stable there isn't really an easy solution to doing this, without requiring a specialized macro for each type/method. So the easiest is to fallback to using a const DEFAULT_BAR and referencing it in the docs (which you wanted to avoid.)


However, there's a rather new nightly feature extended_key_value_attributes (see issue #78835 and PR 78837.)

Besides requiring one of the latest nightly builds. It will also be slightly cumbersome to use for your use case (in its current state). This is because it either requires the use of literals, which excludes using const DEFAULT_BAR. Alternatively, you can use a macro which expands to 5, which is the cumbersome solution.

#![feature(extended_key_value_attributes)]

struct Foo {
    bar: usize,
}

macro_rules! default_bar {
    () => {
        5
    };
}

impl Foo {
    /// Creates a [Foo] with a `bar` of
    #[doc = concat!(default_bar!(), ".")]
    fn new() -> Foo {
        Foo::new_with_bar(default_bar!())
    }

    /// Creates a [Foo] with the provided `bar`.
    fn new_with_bar(bar: usize) -> Foo {
        Foo { bar }
    }
}

The above works on rustc 1.50.0-nightly (bb1fbbf84 2020-12-22)

Note that you have to use concat!, as otherwise default_bar needs to expand into a string. So if you don't need e.g. ".", then just use an empty string, e.g. concat!("", default_bar!()).