tools | docs | tools (rustdoc | attributes)
Documentation is an important part of any project, it allows developers to
explain how to use items within a library as well as communicate the intent of
how to use it through examples. Rust has long championed this feature through
the use of documentation comments and rustdoc
to generate beautiful, easy to
navigate documentation. However, there is no way right now to have documentation
be imported into the code from an external file. This RFC proposes a way to
extend the functionality of Rust to include this ability.
cargo doc
with the
small crate as a dependency and be able to access the contents of the README
without needing to go online to the repo to read it. This also would help
with this issue on
crates.io by making it
easy to have the README in the crate and the crate root at the same.These are just a few reasons as to why we should do this, but the expected outcome of this feature is expected to be positive with little to no downside for a user.
All files included through the attribute will be relative paths from the crate
root directory. Given a file like this stored in docs/example.md
:
# I'm an example
This is a markdown file that gets imported to Rust as a Doc comment.
where src
is in the same directory as docs
. Given code like this:
#[doc(include = "../docs/example.md")]
fn my_func() {
// Hidden implementation
}
It should expand to this at compile time:
#[doc("# I'm an example\nThis is a markdown file that gets imported to Rust as a doc comment.")]
fn my_func() {
// Hidden implementation
}
Which rustdoc
should be able to figure out and use for documentation.
If the code is written like this:
#![doc(include = "../docs/example.md")]
fn my_func() {
// Hidden implementation
}
It should expand out to this at compile time:
#![doc("# I'm an example\nThis is a markdown file that gets imported to Rust as a doc comment.")]
fn my_func() {
// Hidden implementation
}
In the case of this code:
mod example {
#![doc(include = "../docs/example.md")]
fn my_func() {
// Hidden implementation
}
}
It should expand out to:
mod example {
#![doc("# I'm an example\nThis is a markdown file that gets imported to Rust as a doc comment.")]
fn my_func() {
// Hidden implementation
}
}
If you've noticed the path given ../docs/example.md
is a relative path to
src
. This was decided upon as a good first implementation and further RFCs
could be written to expand on what syntax is acceptable for paths. For instance
not being relative to src
.
If a file given to include
is missing then this should trigger a compilation
error as the given file was supposed to be put into the code but for some reason
or other it is not there.
As with all macros being expanded this brings up the question of line numbers
and for documentation tests especially so, to keep things simple for the user
the documentation should be treated separately from the code. Since the
attribute only needs to be expanded with rustdoc
or cargo test
, it should be
ignored by the compiler except for having the proper lines for error messages.
For example if we have this:
#[doc(include = "../docs/example.md")] // Line 1
f my_func() { // Line 2
// Hidden implementation // Line 3
} // Line 4
Then we would have a syntax error on line 2, however the doc comment comes before that. In this case the compiler would ignore the attribute for expansion, but would say that the error occurs on line 2 rather than saying it is line 1 if the attribute is ignored. This makes it easy for the user to spot their error. This same behavior should be observed in the case of inline tests and those in the tests directory.
If we have a documentation test failure the line number should be for the external doc file and the line number where it fails, rather than a line number from the code base itself. Having the numbers for the lines being used because they were inserted into the code for these scenarios would cause confusion and would obfuscate where errors occur, making it harder not easier for end users, making this feature useless if it creates ergonomic overhead like this.
#[doc(include = "file_path")]
is an extension of the current
#[doc = "doc"]
attribute by allowing documentation to exist outside of the
source code. This isn't entirely hard to grasp if one is familiar with
attributes but if not then this syntax vs a ///
or //!
type of comment
could cause confusion. By labeling the attribute as external_doc
, having a
clear path and type (either line
or mod
) then should, at the very least,
provide context as to what's going on and where to find this file for inclusion.
The acceptance of this proposal would minimally impact all levels of Rust users as it is something that provides convenience but is not a necessary thing to learn to use Rust. It should be taught to existing users by updating documentation to show it in use and to include in in the Rust Programming Language book to teach new users. Currently the newest version of The Rust Programming Language book has a section for doc comments that will need to be expanded to show how users can include docs from external sources. The Rust Reference comments section would need to updated to include this new syntax as well.
Currently there already exists a plugin that could be used as a reference and has shown that there is interest. Some limitations though being that it did not have module doc support and it would make doc test failures unclear as to where they happened, which could be solved with better support and intrinsics from the compiler.
This same idea could be implemented as a crate with procedural macros (which are on nightly now) so that others can opt in to this rather than have it be part of the language itself. Docs will remain the same as they always have and will continue to work as is if this alternative is chosen, though this means we limit what we do and do not want rustc/rustdoc to be able to achieve here when it comes to docs.