tools | docs | tools (rustdoc | intra-links)
intra_rustdoc_links
Add a notation how to create relative links in documentation comments (based on Rust item paths) and extend Rustdoc to automatically turn this into working links.
It is good practice in the Rust community to add documentation to all public items of a crate, as the API documentation as rendered by Rustdoc is the main documentation of most libraries. Documentation comments at the module (or crate) level are used to give an overview of the module (or crate) and describe how the items of a crate can be used together. To make navigating the documentation easy, crate authors make these items link to their individual entries in the API docs.
Currently, these links are plain Markdown links, and the URLs are the (relative) paths of the items' pages in the rendered Rustdoc output. This is sadly very fragile in several ways:
../foo/struct.Bar.html
to the first paragraph of the doc comment of the module lorem
will work on the rendered /lorem/index.html
page,
but not on the crate's summary page /index.html
./crate-name/foo/struct.Bar.html
)
to circumvent the previous issue
might work for the author's own hosted version,
but will break when
looking at the documentation using cargo doc --open
(which uses file:///
URLs)
or when using docs.rs.To solve this dilemma, we propose extending Rustdoc to be able to generate relative links that work in all contexts.
Markdown/CommonMark allow writing links in several forms (the names are from the CommonMark spec in version 0.27):
[link text](URL)
(inline link)[link text][link label]
(reference link,
link label can also be omitted, cf. shortcut reference links)
and somewhere else in the document: [link label]: URL
(this part is called link reference definition)<URL>
which will be turned into the equivalent of [URL](URL)
(autolink, required to start with a schema)We propose that
in each occurrence of URL
of inline links and link reference definitions,
it should also be possible to write a Rust path
(as defined in the reference).
Additionally, automatic link reference definitions should be generated
to allow easy linking to obvious targets.
Rust paths as URLs in inline and reference links:
[Iterator](std::iter::Iterator)
[Iterator][iter]
,
and somewhere else in the document: [iter]: std::iter::Iterator
[Iterator]
,
and somewhere else in the document: [Iterator]: std::iter::Iterator
The third syntax example above shows a
shortcut reference link,
which is a reference link
whose link text and link label are the same,
and there exists a link reference definition for that label.
For example: [HashMap]
will be rendered as a link
given a link reference definition like [HashMap]: std::collections::HashMap
.
To make linking to items easier, we introduce "implied link reference definitions":
[std::iter::Iterator]
,
without having a link reference definition for Iterator
anywhere else in the document[`std::iter::Iterator`]
,
without having a link reference definition for Iterator
anywhere else in the document
(same as previous style but with back ticks to format link as inline code)If Rustdoc finds a shortcut reference link
it will add a link reference definition for this link label pointing to the Rust path.
Collapsed reference links ([link label][]
) are handled analogously.
(This was one of the first ideas suggested by CommonMark forum members as well as by Guillaume Gomez.)
These additions are valid Markdown, as defined by the original Markdown syntax definition as well as the CommonMark project. Especially, Rust paths are valid CommonMark link destinations, even with the suffixes described below.
The following:
The offers several ways to fooify [Bars](bars::Bar).
should be rendered as:
The offers several ways to fooify <a href="bars/struct.Bar.html">Bars</a>.
when on the crates index page (index.html
),
and as this
when on the page for the foos
module (foos/index.html
):
The offers several ways to fooify <a href="../bars/struct.Bar.html">Bars</a>.
When using the autolink syntax (<URL>
),
the URL has to be an absolute URI,
i.e., it has to start with an URI scheme.
Thus, it will not be possible to write <Foo>
to link to a Rust item called Foo
that is in scope
(this also conflicts with Markdown ability to contain arbitrary HTML elements).
And while <std::iter::Iterator>
is a valid URI
(treating std:
as the scheme),
to avoid confusion, the RFC does not propose adding any support for autolinks.
This means that this will not render a valid link:
Does not work: <bars::Bar> :(
It will just output what any CommonMark compliant renderer would generate:
Does not work: <a href="bars::Bar">bars::Bar</a> :(
We suggest to use Implied Shortcut Reference Links instead:
Does work: [`bars::Bar`] :)
which will be rendered as
Does work: <a href="../bars/struct.Bar.html"><code>bars::Bar</code></a> :)
The Rust paths used in links are resolved
relative to the item in whose documentation they appear.
Specifically, when using inner doc comments (//!
, /*!
),
the paths are resolved from the inside of the item,
while regular doc comments (///
, /**
) start from the parent scope.
Here's an example:
/// Container for a [Dolor](ipsum::Dolor).
struct Lorem(ipsum::Dolor);
/// Contains various things, mostly [Dolor](ipsum::Dolor) and a helper function,
/// [sit](ipsum::sit).
mod ipsum {
pub struct Dolor;
/// Takes a [Dolor] and does things.
pub fn sit(d: Dolor) {}
}
mod amet {
//! Helper types, can be used with the [ipsum](super::ipsum) module.
}
And here's an edge case:
use foo::Iterator;
/// Uses `[Iterator]`. <- This resolves to `foo::Iterator` because it starts
/// at the same scope as `foo1`.
fn foo1() { }
fn foo2() {
//! Uses `[Iterator]`. <- This resolves to `bar::Iterator` because it starts
//! with the inner scope of `foo2`'s body.
use bar::Iterator;
}
If an item is re-exported from an inner crate to an outer crate,
its documentation will be resolved the same in both crates, as if it were in
the original scope. For example, this function will link to f
in both crates,
even though f
is not in scope in the outer crate:
// inner-crate
pub fn f() {}
/// This links to [f].
pub fn g() {}
// outer-crate
pub use inner_crate::g;
If a public item links to a private one, and --document-private-items
is not passed,
rustdoc should give a warning. If a private item links to another private
item, no warning should be emitted. If a public item links to another private
item and --document-private-items
is passed, rustdoc should emit the link,
but it is up to the implementation whether to give a warning.
Rust has three different namespaces that items can be in, types, values, and macros. That means that in a given source file, three items with the same name can be used, as long as they are in different namespaces.
To illustrate, in the following example
we introduce an item called FOO
in each namespace:
pub trait FOO {}
pub const FOO: i32 = 42;
macro_rules! FOO { () => () }
To be able to link to each item, we'll need a way to disambiguate the namespaces. Our proposal is this:
Look at the [FOO] trait
. This also
applies to modules and tuple structs which exist in both namespaces.
Rustdoc will throw a warning if you use a non-disambiguated path in
the case of there being a value in both the type and value namespace.type@
,
e.g., See [type@foo]
. This will work for structs, enums, mods, traits,
and unions.struct
s can be prefixed with struct@
,
e.g., See [struct@Foo]
.enum
s can be prefixed with enum@
,
e.g., See [enum@foo]
.mod@
,
e.g., See [mod@foo]
.trait@
,
e.g., See [trait@foo]
.union@
,
e.g., See [union@foo]
.struct@
to refer to an enum),
but do not rely on this.mod@
or module@
, e.g. [module@foo]
!
,
e.g., Look at the [FOO!] macro
. You can alternatively use a macro@
prefix,
e.g. [macro@foo]
value@
,
e.g., See [value@foo]
.()
suffix,
e.g., Also see the [foo()] function
. You can also use function@
, fn@
,
or method@
.const@
,
e.g., As defined in [const@FOO].
static@
,
e.g., See [static@FOO]
.static@
to refer to a const),If a disambiguator for a type does not match, rustdoc should issue a warning.
For example, given struct@Foo
, attempting to link to it using [enum@Foo]
should not be allowed.
Ideally, Rustdoc would be able to recognize Rust path syntax, and if the path cannot be resolved, print a warning (or an error). These diagnostic messages should highlight the specific link that Rustdoc was not able to resolve, using the original Markdown source from the comment and correct line numbers.
(Excerpt from Diesel's expression
module.)
// diesel/src/expression/mod.rs
//! AST types representing various typed SQL expressions. Almost all types
//! implement either [`Expression`] or [`AsExpression`].
/// Represents a typed fragment of SQL. Apps should not need to implement this
/// type directly, but it may be common to use this as type boundaries.
/// Libraries should consider using [`infix_predicate!`] or
/// [`postfix_predicate!`] instead of implementing this directly.
pub trait Expression {
type SqlType;
}
/// Describes how a type can be represented as an expression for a given type.
/// These types couldn't just implement [`Expression`] directly, as many things
/// can be used as an expression of multiple types. ([`String`] for example, can
/// be used as either [`VarChar`] or [`Text`]).
///
/// [`VarChar`]: diesel::types::VarChar
/// [`Text`]: diesel::types::Text
pub trait AsExpression<T> {
type Expression: Expression<SqlType=T>;
fn as_expression(self) -> Self::Expression;
}
Please note:
VarChar
and Text
),
which are not in scope and need to be linked to by their absolute Rust path.
To make reading easier and less noisy, reference links are used to rename the links.
(An assumption is that most readers will recognize these names and know they are part of diesel::types
.)To link to the fields of a struct
we propose to write the path to the struct,
followed by a dot, followed by the field name.
For example:
This is stored in the [`size`](storage::Filesystem.size) field.
To link to the variants of an enum
,
we propose to write the path to the enum,
followed by two colons, followed by the field name,
just like use Foo::Bar
can be used to import the Bar
variant of an enum Foo
.
For example:
For custom settings, supply the [`Custom`](storage::Engine::Other) field.
To link to associated items,
i.e., the associated functions, types, and constants of a trait,
we propose to write the path to the trait,
followed by two colons, followed by the associated item's name.
It may be necessary to use fully-qualified paths
(cf. the reference's section on disambiguating function calls),
like See the [<Foo as Bar>::bar()] method
.
We have yet to analyze in which cases this is necessary,
and what syntax should be used.
If linking to an associated item that comes from a trait, the link should only be resolved in the trait is in scope. This prevents ambiguities if multiple traits are available with the associated item. For example, this should issue a warning:
#[derive(Debug)]
/// Link to [S::fmt]
struct S;`
but this should link to the implementation of Debug::fmt
for S
:
use std::fmt::Debug;
#[derive(Debug)]
/// Link to [S::fmt]
struct S;`
Currently, Rustdoc is able to link to external crates,
and renders documentation for all dependencies by default.
Referencing the standard library (or core
)
generates links with a well-known base path,
e.g. https://doc.rust-lang.org/nightly/
.
Referencing other external crates
links to the pages Rustdoc has already rendered (or will render) for them.
Special flags (e.g. cargo doc --no-deps
) will not change this behavior.
We propose to generalize this approach by adding parameters to rustdoc that allow overwriting the base URLs it used for external crate links. (These parameters will at first be supplied as CLI flags but could also be given via a config file, environment variables, or other means in the future.)
We suggest the following syntax:
rustdoc --extern-base-url="regex=https://docs.rs/regex/0.2.2/regex/" [...]
By default, the core/std libraries should have a default base URL set to the latest known Rust release when the version of rustdoc was built.
In addition to that,
cargo doc
may be extended with CLI flags
to allow shortcuts to some common usages.
E.g., a --external-docs
flag may add base URLs using docs.rs
for all crates that are from the crates.io repository
(docs.rs automatically renders documentation for crates published to crates.io).
Automatically linking to external docs has the following known tradeoffs:
Prefix Rust paths with a URI scheme, e.g. rust:
(cf. path ambiguities).
Prefix Rust paths with a URI scheme for the item type, e.g. struct:
, enum:
, trait:
, or fn:
.
javadoc and jsdoc
use {@link java.awt.Panel}
or [link text]{@link namepathOrURL}