After years of tinkering with Rust, I simply cannot get over documenting structs and enums in rustdoc.
What rustdoc wants:
/// Jobbler for jobbling the thing.
struct ThingJobbler
{
/// Doodad by which the thing is jobbled.
doodad: Doodad,
/// Exraneous nonsense.
whatsit: Stuff,
/// Thing to be jobbled.
gadget: Thing,
/// Way out.
///
/// This portal must have been painted prior to use or else something,
/// something, something.
portal: Gate,
}
What I want
/** Jobbler for jobbling the thing.
*
* - doodad: Doodad by which the thing is jobbled.
* - whatsit: Exraneous nonsense.
* - gadget: Thing to be jobbled.
* - portal: Way out.
* This portal must have been painted prior to use or else
* something, something, something.
*/
struct ThingJobbler
{
doodad: Doodad,
whatsit: Stuff,
gadget: Thing,
portal: Gate,
}
I am looking for a way to do something more like the second way, but with the HTML output of the first. To me, this is orders of magnitude cleaner and categorically better in the actual code; you cannot convince me otherwise.
Is this possible with rustdoc? If not, what can I use instead of rustdoc.
You could write an attribute macro that transforms the documentation in the way you want. You would have to use the macro on every struct with this format of documentation, but other than that, it would straightforwardly work with rustdoc.
I'm with you!
Having started Rust just weeks ago, I was blasted away how easy rustdoc was to use and how nice and exhaustive the output was.
So, after having written thousands of pages of C-code documentation with doxygen, I went ahead. And was disappointed. You can't document function parameters and return results in a systematic way. Description of enums and struct's fields ... fail. At least, I found no description.
This is just a snippet of my C-code with doxygen:
/**
\brief Initializes an instance instantiated with APP_FormatterInstantiate().
\param instance is the instance number.
\param myMail is the instance's mailbox ID.
\param masterMail is the master's (left hand side) mailbox ID.
\param slaveMail is the slave's (right hand side) mailbox ID.
\return true on success.
*/
bool APP_FormatterInitialize(unsigned int instance, uint8_t myMail, uint8_t masterMail, uint8_t slaveMail);
or:
/**
\brief Data for a single instance.
*/
typedef struct {
APP_FormatterStates_t state; ///< State of FSM
APP_FormatterTypes_t fmtType; ///< Type of formatting task.
APP_MailboxId_t myMail; ///< Task's mailbox ID
APP_MailboxId_t masterMail; ///< Task's master (left hand side).
APP_MailboxId_t slaveMail; ///< Task's slave (right hand side)
...
This generates a nice output with a short description of each enum / parameter. Why can't rustdoc be at least as good as doxygen? rustdoc shines with the integration into the whole ecosystem and with all the references to crate methods. But developers want parameter descriptions that are not in a single block of text, but in list form.
Or is there a way that haven't found out yet? Not that I could find anything in the otherwise excellent Rust documentation of crates.
I think the idiomatic Rust way (heavily influenced by the functional tradition in ocaml and haskell) would be to use "self documenting" parameters and return types, in a way that makes misusing the function quasi impossible and written documentation less relevant.
Using types that are correct by construction helps greatly with that way of doing things.
Having per-parameter doc comments would be occasionally useful, but not that often. It should in general be obvious what each parameter is for, documented by their type, name, and the function’s doc. Feeling the need to document parameters one by one is usually a smell, a sign that the interface could be tighter and more obvious.
Wrt structs and enums, not sure what you mean. Rustdoc understands field and variant doc comments perfectly well:
struct Foo {
/// Some hopefully **useful** info and not just
/// repeating what the name and type already say
bar: Bar,
/// And so on
baz: Baz,
}
So, continuing your argument, the requirement by Rust's guidlines to include documentation and examples is fishy. This only shows that the interface is not selfexplanatory.
I have heard the argument "the code is self-explaining" since decades. And still, documentation tools evolved and have their place.
And to add some icing to the top, doxygen generates call-graphs too:
Not saying that Rustdoc has to be exactly the same. But showing how exhaustive things can get.
I don't think that '< x >' (remove blanks) is part of the markdown language. Markdown Reference
Anyhow, dustdoc completely removed my comment. The "bad" comment or "HTML" even is gone in the generated HTML (by looking at the HTML-source).
I'd have to write it like this:
/// <x/>
XMLTagEmpty,
I don't think this increases readability. But funny enough, rustdoc lets pass a '< b >' (remove blanks), despite the fact that bold is '_ bold _' or '* bold *' (remove blanks again) in markdown. That's the result of not separating domains. Markdown-domain and propably/supposedly/assumptional/misunderstood HTML.
An other point of Rust and documentation and Rust's formatting is, that it inserts newlines.
Starting with this:
enum Example {
One, /// useless in-line comment
}
It decides to insert a newline to get this:
enum Example {
One,
/// Useless comment
}
and then complains about a comment that doesn't comment an item in the enum. I want my code to be compact. And not to double the line count. I'm not paid by LOC (lines of code).
So the effect of these decissions is, that I will not '///' comment my short hints to about what is going on. I see no gain why rustdok makes a goulasch of markdown and HTML. Use markdown and escape anything that might be invalid HTML within a comment.
doxygen solved this problem by a '///< comment'. Meaning: This comment is for the line it is in.
Sad sidenote: doxygen doesn't support Rust (anymore?)
Doc comment /// comment in Rust are syntax sugar for a macro #[doc = "comment"] that is part of the language, you can't just place them anywhere you want.
HTML source or browser devtools? If it's the latter, they might have "cleaned up" HTML for you, by e.g. removing closing tags which have no opening counterpart.
It's not that it "inserts newlines", it's just that the order is important. Doc comments are a form of attributes[1], and (outer) attributes are always associated with the item after it, by definition.
Literally, you can use#[doc("something") instead of /// something - and by the way, the former can go on the same line as the item it documents ↩︎
I already showed a solution how this could be handled. With no negative influence to the current behaviour. And the fact that doxygen handles it, only shows that comments like these are common place Moving the comment to a new line only adds noise.
Just be more relaxed.
Ah thanks, I just looked at the summary. My pitty!
It was devtools.
My source is re-formatted by inserting a newline. And adding noise. I'm doing software since 1977 (yes, I'm an old fart). And I have seen different styles of formatting (of source and comments - if there were any). I prefer a gentle style that isn't as intrusive as Rust's. Unfortunately, there is no way to opt out of some features.
I'm not saying that things have to be strictly as I say. The suggestion of the initial post is acceptable.
But still, parameters do lack comments. And yes, I have read the discussion someone linked to (thanks, interesting read). But the only conclusion was that parameter comments lead to useless comments. But how about just ignoring them and so not use them? But no, it is better to block any different attitudes completely. The new inclusiveness I guess.
It's a tool, not a law. Think about it.
Related to this discussion in a much wider perspective: Who has read D. Knuth's book "Literate programming"?
Edit: Oh, I forgot! Thanks for the tip to escape with a leading '/'
No, that's just how open source works. Nobody's interested in implementing that. So it doesn't happen.
And why nobody's interested: because as I wrote earlier Rust has roots in ocaml where using strong valid by construction types is the norm. I'm not saying this is right or wrong. I'm saying there's a cultural mismatch here.
That's quite funny!
OCaml is best in obfuscation. You have the .ml where it is common not to specify the types of parameters. Unless OCaml fails to guess what it is. This happens often enough. But you can (and should) have meaningful parameter names. Until you write the .mli that only shows the types and no parameter names. The result? Put back a meaning into the signature, repeat (copy & paste) the parameter names. Furthermore, .ml and .mli are quite detached. They are not directly linked together. Instead of having some include mechanism, the compiler only finds out at a leter time that .ml and .mli don't match.
Just have a look at the lousy documentation of OCaml-modules to see what you get. OCaml actively discourages documentation.
The output of OCaml's doc-generator is an eyesore and useless.
You could prototype this (and thus show the costs and benefits) using an attribute macro as suggested by @kpreid. Your code would look like:
#[my_doc_enhancer]
/** Jobbler for jobbling the thing.
*
* - doodad: Doodad by which the thing is jobbled.
* - whatsit: Exraneous nonsense.
* - gadget: Thing to be jobbled.
* - portal: Way out.
* This portal must have been painted prior to use or else
* something, something, something.
*/
struct ThingJobbler
{
doodad: Doodad,
whatsit: Stuff,
gadget: Thing,
portal: Gate,
}
Then, using syn's support for reading doc comments, you'd pick up your doc comment, and rewrite the struct as seen by the compiler from one big doc comment before the struct to something in current RustDoc format:
#[doc = r"Jobbler for jobbling the thing."]
#[doc = r""]
struct ThingJobbler
{
#[doc = r"Doodad by which the thing is jobbled."]
doodad: Doodad,
#[doc = r"Exraneous nonsense."]
whatsit: Stuff,
#[doc = r"Thing to be jobbled."]
gadget: Thing,
#[doc = r"Way out."]
#[doc = r"This portal must have been painted prior to use or else"]
#[doc = r"something, something, something."]
portal: Gate,
}
You may benefit from using quote to write out the second form. Guide to Rust procedural macros | developerlife.com has some guidance on writing this sort of proc macro - you want to read the text, skip examples 1 through 4 (not relevant to this use case), and move onto example 5 to see an example of reading in an Item and writing out something based on it.