Search code examples
rustdoccargo-doc

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


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.


Solution

  • 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: