One crucial thing about Rust's documentation is that your examples are run by the test infrastructure.
This makes logical sense. If the programmer provided an example of how to call some_function() then we should test that example works. If it doesn't work their example was wrong, or some_function() is broken, either way the programmer needs to fix that.
For the test infrastructure to run your examples, they must be code, not some sort of annotation.
The result is now you don't ship documentation which oops, is documenting the old some_function which had different behaviour and so of course that example doesn't work any more. Sorry, we didn't rewrite the docs yet, just go read this Discord thread and the six bug tracker entries it links...
I first learned this from python docstrings, which can include testable examples.
I think rust improves on how it’s achieved, but I’m more familiar with how go does it.
Whichever language you use I agree that it’s a great way to ensure that the docs match the package. One thing I would love to see more of is annotations in docstrings that specify when changes were made to packages. (E.g. “version 2.3, added FooFrob()”) Python standard lib does a good job of this, but I can’t think of a convenient (for developers) way to enforce this with tooling.
Asking for your Vec to use a different allocator instead of the global heap allocator is a nightly feature, ask for feature "allocator_api" in your nightly compiler, or read GitHub issue 32838 if you find problems.
#[rustc_const_stable(feature = "const_vec_new", since = "1.39.0")]
Vec::new() is compile time constant from 1.39.0, before that a call to Vec::new() actually calls that new() function at runtime, even though a new Vec doesn't allocate anywhere to put anything. If you're currenty in a compile time constant context Vec::new() won't compile at all until 1.39.0
Well, rustdoc lets you include arbitrary Markdown files. So that case is not eschewed.
With that said, they serve two different use cases. So as someone who has probably written more lines of docs than I have code in Rust, it doesn't make sense to me to ask for one and not the other.
API docs with a bit of high level prose and some examples are very likely good enough for small focused libraries.
For bigger libraries or projects, you are absolutely free to write prose that is disconnected from the API. You can do that in rustdoc or use some other tool (mdbook is very popular).
But either way, you pretty much always want at least some API docs.
> Doing it that way repeats the mistake of HTML. Annotation should be separate from content, not made into a sausage with it.
This doesn't make any sense to me. The API of a library is both the signatures of items exported by the library and the documentation on those items. The docs might make certain guarantees about behavior, error/panic conditions and more. Those can't be described by the type system, and thus, the items and the docs are intricately connected. Both are required to define a library's API.
> I'm all for good documentation, but why embed it in the source code? Why not have good documentation files that stand alone?
It usually ends up out of sync with the code almost immediately. And there is a very good chance of the documentation being lost, buried in some some enterprise document management system. Sometimes I feel like Gandalf reading through stacks of scrolls trying to find thousand year old references to magical rings.
I'm not sure if rust does this but C#'s embedded docs are integrated throughout the toolchain so they appear alongside intellisense as your writing or navigating etc. Keeping them with the code makes sense to me because they have the same reasons to change.
My experience with most rust crates is that they do use separate markdown files for their big doc pages like readme and tutorial, getting started etc; and they include (`!#doc["readme.md"]`, or something like that) these files in their source so that they're picked up by `cargo doc`. Only actual Rust items get directly-in-source-documentation -- again, in general.
However I agree (I think) that markdown is not a great format for long form docs; you want something a bit more expressive (say, asciidoctor).
As an example of documentation at the same or better standard, also look at Racket. I'd say the python ecosystem is also really getting better at this in the past few years.
rustdoc may be good for reference documentation (as a Rust noob, I don't really think it is, most docs are pretty messy and the UX isn't very good, it's often better/easier to read the docs in-situ in the code), but it seems to lack any and all facilities for non-refence docs, which most Rust projects basically don't have at all, some put in the top-level module docs (which usually limits the size) and a few have completely separated docs for anything non-reference. (Or one of the creative hacks to get around it, like creating empty public modules or useless macros to have additional pages).
In these areas rustdoc is a significant regression compared to say, Sphinx.
The upside of rustdoc is no settings, no setup, no friction. Which also means that the deficiencies as far as the reference functionality is concerned can actually be fixed.
> it's often better/easier to read the docs in-situ in the code
Could you elaborate on this? It's really surprising to me. Some reasons why I'm surprised to hear you say it:
* The actual public API isn't necessarily always obviously clear from the source code. For example, if you see 'pub struct Foo;', that doesn't mean it's actually in the public API of the library. rustdoc takes care of figuring out what exactly is exported.
* Viewing it in a web browser has the benefit of it being nicely formatted.
* The web browser also has hyperlinks you can follow. Your editor environment probably handles a fair bit of these with 'goto definition', but the prose itself might have hyperlinks that aren't easily followed in the context of your editor.
I think the first reason is the most compelling. rustdoc gives you a simple overview at a glance of what's actually in the public API. Discerning this from the source as a human isn't terribly difficult, but not as easy as a glance.
> but it seems to lack any and all facilities for non-refence docs, which most Rust projects basically don't have at all, some put in the top-level module docs (which usually limits the size)
I don't really think any of those are deserving of non-reference docs.
I do think I have some crates that would be deserving of non-reference docs but haven't been able to find the time to write them. It isn't for lack of tooling.
> a few have completely separated docs for anything non-reference
Do you see this as a problem? If so, why? Aren't non-reference docs, by definition, totally separate from reference docs?
> (Or one of the creative hacks to get around it, like creating empty public modules or useless macros to have additional pages).
I've used the empty public module approach before. Other than it being not quite an intended workflow, it has worked well. What specifically do you see wrong with it?
> * The actual public API isn't necessarily always obviously clear from the source code. For example, if you see 'pub struct Foo;', that doesn't mean it's actually in the public API of the library. rustdoc takes care of figuring out what exactly is exported.
That's true, but hasn't really bitten me too much so far. In most cases it seems like if you managed to Ctrl+B some place and see "pub" it's probably actually public. The same goes for sorting by visibility in the structure browser, I'm assuming that just looks at the local visibility and not whether it's visible from outside the crate. It'd be cool if CLion could visually mark stuff that's not part of the crate API.
> * Viewing it in a web browser has the benefit of it being nicely formatted.
> * The web browser also has hyperlinks you can follow. Your editor environment probably handles a fair bit of these with 'goto definition', but the prose itself might have hyperlinks that aren't easily followed in the context of your editor.
CLion/their Rust plugin renders doc-comments as inline rich text, complete with clickable references, so for these two points it's pretty much the same as rustdoc. In rustdoc I constantly get lost with more complex types because it's just a huge page which might have a lot of trait impls on it and if you're in the middle of it there really isn't anything telling you what you are looking at right now (e.g. whose trait methods). In CLion I have my breadcrumbs that always tell me where I am.
Maybe sticky breadcrumbs would help with getting lost on larger pages.
> Do you see this as a problem? If so, why? Aren't non-reference docs, by definition, totally separate from reference docs?
Surely it means there is a lot more friction to doing it? You have to setup a separate documentation tool, builds and hosting for that, probably need to figure out how to configure it for linking into the reference etc.
> Do you think every single Rust library should have non-reference docs?
No, but to me it seems like Rust projects tend to have a lot less of it relative to other ecosystems. I've also seen a bunch of crates that don't have even one example in them, even though the code itself looked solid.
> What specifically do you see wrong with it?
It appearing as modules and navigation happening through the module table is kinda awkward but otherwise it's... fine? Which I suppose means that rustdoc is mostly there.
-
A general issue I've experienced with some crates is that (especially if there are only some API docs and nothing else) it's hard to get that "at a glance" overview of the API. Questions like "I wanna do X, what do I need for that" or "I have an X, and need a Y, how do I go from one to the other" aren't particularly obvious. It's often unclear to me from rustdoc (without trying to compile) what types can convert via From/Into into other types, particularly if crates bring their own variations of From traits into the mix. Doxygen used to do all these graphs, I mostly found them kinda useless, though the inheritance ones were sometimes useful. Rustdoc doesn't do anything in the visual-ish domain so far, which might be because that's proven to be a largely useless fad in prior tools, but perhaps Rust is sufficiently different from C++ that there could be more-than-useless visualizations.
A concrete example where I sorta experienced this with the mysql crate. There's Queryable, okay, and you can see Conn implements that, so far so good. How do I get one of those? Conn::new is a thing, but that's a generic over "things intoable into some other type" (sorta Yoda-style as the "Opts: TryFrom<T, Error = E>" constraint). If I click on the TryFrom there that leads nowhere [1]. Clicking on Opts (apparently you can put trait bounds on non-type parameters, TIL) brings up the Opts page, and that has a little comment telling us to use OptsBuilder to make one of these. But there's also an inconspicuous "TryFrom<&'_ str>" down in the sidebar. The docs are silent on this, but that actually calls Opts::from_url, which is also not that documented but I can guess what it does. So now I've actually managed to figure out that I could have just done Conn::new("user:pass@host") all along (except that won't work because it wants mysql:// because URL, right? Maybe? I dunno, would have to try). Now, one might point out that the first lines of non-boilerplate in the example are this:
let url = "mysql://root:password@localhost:3307/db_name";
let pool = Pool::new(url)?;
let mut conn = pool.get_conn()?;
And yeah, that totally gets you a Conn, right? Well PooledConn or whatever, but same difference. But I didn't want a pool, I just wanted to connect. I also can't click on anything in the examples (if I read this in my IDE, I actually can go to definition in all examples).
Later it was time for writing some queries and of course the question became "well, uh, what types can I stuff in there? And when I query, what types can I convert to, and what does it expect for these on the other side?". Just from rustdoc itself this is difficult to understand (the 3rd question is of course not really answerable for rustdoc, as it lies outside the type system). There's actually a hand-written table to specifically answer these questions, but that's part of a dependency: https://docs.rs/mysql_common/latest/mysql_common/#supported-... mysql actually has a very comprehensive overview in the crate root, it's just not directly see this as there is no table of contents for the contents of the page itself -- which makes sense for rustdoc, where, from the API docs PoV, you're not expected to put pages worth of content into a single module/struct/function docstring, but would be weird to be absent from a generalist documentation system. Headlines are anchors, so I'm guessing you can always create your own hand-made ToC here, but that doesn't seem ideal to me. People do that with README.md and it's not great imho.
Just to clarify, I'm not trying to shit on mysql or mysql_common here, obviously.
[1] I just thought that maybe if you build the docs locally with "cargo d" that it'd also generate std docs, but they only link to the online docs. "cargo d" also doesn't tell you where the built docs are and there isn't an overall index.
RE browser vs reading the code: sounds like you have a nicer setup than my neovim setup. Although I think my first point still holds unless CLion handles that case too.
With respect to the rest of your comment, indeed, those are issues. Although I think I take issue with you pinning this on rustdoc. I actually think it's a dance between documentation presentation (so, rustdoc), API design and familiarity with the language.
I've long said that rustdoc makes unknown unknowns difficult to discover, and this is particularly painful for folks new to Rust. Because you don't really know what to look for yet. And writing docs is a difficult exercise in perspective taking, where you need to balance what you think others know. If you assume they know too little, it's not hard to end up writing too much and adding a fair bit of noise. With that said, I agree that "too little docs" is a far more common problem than "too many docs."
But yeah, your experience is a perfect example of what I mean when I say "generics complicate APIs." They introduce indirection everywhere, and I'm not sure how much rustdoc can really help with that. You might be right that maybe there are some visualizations that can be added, but like you, I've always seen those as gimmicks in other tools that are rarely useful. IMO, a heavily generic API really requires the crate author to write more prose about how their APIs are intended to be used with lots of concrete examples. But that's really hard to do, because writing good docs requires being a good communicator, and most of us suck hard at that.
The interesting bit here is that I've personally found the documentation experience in Rust to be far far better than any other ecosystem. All the way from writing docs up to consuming them. I've sampled many different ecosystems (C, C++, Haskell, Python, Go to name some) and other than maybe Go, I thought the doc experience was really just not great in any of them. Python specifically seems to be a case where I tend to see a lot of variance in opinion. I hated Sphinx so much, for example, that I built an alternative.[1] (Well, alternative for API docs, pdoc does not deal with the non-reference case.) I also just generally dislike the output that Sphinx produces. I find that it lacks structure, and I've always had a hard time navigating my way through Python library docs.
I'm all for good documentation, but why embed it in the source code? Why not have good documentation files that stand alone?
Doing it that way repeats the mistake of HTML. Annotation should be separate from content, not made into a sausage with it.