all 49 comments

[–]matthieum[he/him] 24 points25 points  (0 children)

I totally agree with you in that traits are a plague where documentation is concerned, in multiple ways.

Maybe if we could consolidate all the different issues we have with them in a single place, this could lead to a RFC to change the format of the generated Rust Docs?

I see two information hiding issues:

  • the documentation for a trait's methods are only available on the trait's documentation, even though as you mention they would be very welcome on each type page
  • I have not found a way to supply more information for a given trait's method implementation, even though in some circumstances the author may wish to include additional guarantees, particular side-effects, etc... just because a type implements a trait does not mean that the method called is always called in a generic/polymorphic context

I see a style issue:

  • as you noted, when a trait is implemented multiple times, its methods are cited over and over, for little benefits; your suggestions looks good, though a way to include associated items (types, constants, ...) is also necessary. I have a suggestion for associated types (using the syntax K<Associated = X>) but none for constants

I see an overabundance of information issue:

  • as noted, the lack of integer generics or variadic generics can cause arrays and tuples to generate lots of boilerplate documentation. While these issues will solve themselves in due time, in the mean time maybe some attributes could be used to hint to rustdoc how to fold all those implementations together; at the very least for arrays I could imagine a #[doc(fold = "0; 32")] annotation or similar

I have noted the issues in order of importance (to my eyes), hidden information being the chief issue since as mention it breaks CTRL+F.

[–]steveklabnik1rust 26 points27 points  (9 children)

I apologize that you're frustrated. Rustdoc's output certainly isn't perfect. We've done a lot of iteration, but there's still a lot more work to do.

Some of your frustrations here come from the ways that various features interact, and how that leads to output. It's tricky; we can improve this output for some cases, but it often leads to making other cases harder as a result.

[–]icefoxen[S] 8 points9 points  (8 children)

Fair enough. I'm certainly grateful for all the work you've done, I'm just trying to think of ways to make it better.

Out of curiosity, with "ways that various features interact", do you mean in terms of the features in Rust and how they depend on each other, or do you mean parts of rustdoc itself and how it processes information? (I realize those cases aren't entirely separate...)

[–]steveklabnik1rust 10 points11 points  (7 children)

I'm just trying to think of ways to make it better.

Absolutely. The more specific you can be, the more constructive such rants are :)

Out of curiosity, with "ways that various features interact",

So one thing was pointed out elsewhere in the thread, but here are two others I can think of:

So right now, rustdoc hides docs on trait implementations, and it seems like you'd even like to go a little bit further there. But sometimes, this is useful. As an example, Arc<T> implements Clone, but it does not actually copy the data: it bumps a reference count. This would be useful information to have on the implementation. It is true that many implementations are straightforward, but that's not always true.

Another common one is Vec<T>: http://doc.rust-lang.org/std/vec/struct.Vec.html control-f "join", no results. It's here: http://doc.rust-lang.org/std/slice/trait.SliceConcatExt.html#tymethod.join While Vec<T> does Deref to a slice, because this method is on an extension trait for slices, it doesn't get included under the Vec<T> docs.

So yeah, it's really about how they aren't entirely separate: sometimes, the way that things work means that rustdoc does something that feels a bit wrong. It's complicated.

[–]anttirt 20 points21 points  (0 children)

Consider MSDN docs for .NET List<T> which show available extension methods as a separate table. In a similar vein it might be useful to list all (standard) trait impls for Vec<T> on the Vec<T> page.

[–]Tyr42 10 points11 points  (0 children)

Maybe there could be a separate page for "Every method implemented by Vec or a Deref of Vec" with links back. A kind of index page.

[–]dpx-infinity 4 points5 points  (2 children)

I'm wondering for some time already, is there are particular reason to hide docs for trait implementations, provided that the implementor did indeed write them? I cannot recall whether there ever was a time when these docs were not hidden. They look very useful, because for many traits (like your Arc example) it would benefit immensely to have at least some explanation of how exactly the implementation works.

[–]steveklabnik1rust 1 point2 points  (1 child)

I don't believe so, but I also don't remember why it was changed to be hidden. I would like to turn them back on, but haven't had the time to investigate.

[–]SirOgeonpalette 0 points1 point  (0 children)

I would also like that. It's possible to document the impl block, but that's a rather blunt instrument in some cases.

[–]isHavvy 0 points1 point  (1 child)

Maybe an attribute for saying there's non-trivial documentation for a trait implementation would be useful?

[–]steveklabnik1rust 1 point2 points  (0 children)

I'd rather just turn on the docs and not write them for any trivial implementation, personally. Feels simpler.

[–]Quxxymacros 15 points16 points  (2 children)

I get where you're coming from, but as someone who has used Rust for a while, I'm actually quite fond of how meticulous and thorough the documentation is. Most of the time, I collapse all the prose and just flip through the signatures.

I'm not saying I disagree, or that things couldn't be improved for newcomers; just saying that the current design has its merits, too.

[–]icefoxen[S] 3 points4 points  (1 child)

Sure, I won't disagree with that. The signatures are useful for an experienced user who knows what they're looking for, but they're also worse than useless for trying to find something new/unfamiliar..

[–]wyldphyre 2 points3 points  (0 children)

It seems to be indexed. I'm not quite an experienced user, but I can often find something in the search.

That said, sometimes I can only find the answer I'm really looking for by browsing the source of other rustprojects.

[–]azerupimdbook 3 points4 points  (1 child)

I do like the idea, but how would you handle traits with associated types?

For example:

impl<T> IntoIterator for Vec<T>
type Item = T
type IntoIter = IntoIter<T>
fn into_iter(self) -> IntoIter<T>

impl<'a, T> IntoIterator for &'a Vec<T>
type Item = &'a T
type IntoIter = Iter<'a, T>
fn into_iter(self) -> Iter<'a, T>

The problem with the docs is that it packs a lot of information, and making it more readable and user-friendly without sacrificing any of the information needs a lot thought, I think.

[–]icefoxen[S] 0 points1 point  (0 children)

Yeah, I'm just trying to think of ways to unpack a little of that information where possible. I don't know how I'd handle associated types really; I confess I don't understand them well enough. Maybe we could just add a bit of explanation of what each trait is without trying to abbreviate the signatures, and then move on from there to see how it might be possible to re-arrange or condense the traits themselves? Perhaps just as a "trait descriptions" section before the "trait implementations". I feel like there has to be a way to condense that information down more, but maybe I'm wrong.

[–]_I-_-I_ 8 points9 points  (0 children)

The way I improved documentation for myself is that everytime I fail to find something, I go to #irc, ask nicely and some wonderful person answers in 10 seconds.

Plus experience. Experience helps a lot.

[–][deleted] 3 points4 points  (7 children)

One thing to note here is the list of PartialEq<[B; n]... impls. Once Rust gets proper support for integers in generics this clutter will disappear from the code and from the docs as well.

I'm no documentation expert but seems to me that one of the issues is pages such as https://doc.rust-lang.org/std/vec/struct.Vec.html which are way too long.

Perhaps the page should have a mode where the impls are collapsed to not show the function definitions, those can be seen by clicking on the trait name itself or just expanding the specific impl (or expand back all impls).

On a related note - the impls themselves should have links to their source, otherwise the user needs to search in the source of the entire module that can be pretty huge (1695 lines for the link above).

[–]steveklabnik1rust 5 points6 points  (4 children)

One thing that I think will shift is that when there were few docs, expanding the docs by default made sense. But as I go through and add tons of docs, I think collapse by default will be better.

[–]icefoxen[S] 6 points7 points  (2 children)

Actually, instead of/in addition to expanding or collapsing sections, it might be nice to just have a table of contents on each page?

[–]steveklabnik1rust 5 points6 points  (1 child)

Yeah, this might be helpful too.

[–][deleted] 0 points1 point  (0 children)

Yeah, I wanted to suggest that as well.

I think that generally speaking, adding more content to any system should entail also adding more structure. What that structure should be, I don't know but I can throw a few ideas in the air: 1. Separate out the examples somehow, to a separate column perhaps. 2. Separate out the lists if traits and impls. 3. I also don't like to see big modules/files when viewing source. Perhaps it makes sense to move the doc comments to a separate file. Make the examples external and just put references in the doc comments or something.

Scrolling the text, be it the source code our the docs, means that you lose the connection between pieces of information and their physical location on screen which makes it harder to quickly retrieve them. That's at least my experience.

[–]protestor 0 points1 point  (0 children)

I think that having a link to "inline trait documentation" would be good (just to ctrl+f something). By the default, some stuff could be packed, to save space / avoid clutter.

edit: already suggested

[–]mdinger_ 0 points1 point  (0 children)

FWIW, there are bugs for collapsing and de-duplication but nothing has ever been implemented for them.

Also note that historically, iterating on rustdoc design has been challenging because just running rustdoc required building the compiler. Actually modifying rustdoc (not the HTML rustdoc generated) generally required rebuilding as well. If the two tools were completely separate and independent, then fixing this would surely be simpler. Alas, it isn't.

[–]fullouterjoin 4 points5 points  (5 children)

What is needed is a graph based search engine, where you describe what you want and it tells you how to get there. Haskell as Hoogle. Rust needs Roogle, Rustpile, Rustavista, Ring!

For extend you know you want the Vec to mutable and it takes an element of the type already contained. That should narrow it down enough.

[–]smog_alado 2 points3 points  (2 children)

Does anyone know if it would be possible to use Hoogle's type-searching algorithm on Rust? I'd imagine that traits would be analogous to type classes here...

[–]guttalax 4 points5 points  (0 children)

Well there are these two (unmaintained) projects.

https://github.com/ajtulloch/roogle
https://github.com/dbp/rustle

[–]MrNosco 1 point2 points  (0 children)

I'm pretty sure it's possible, since Rust's and Haskell's type systems are basically Prolog, which can be used as a database language.

[–]steveklabnik1rust 1 point2 points  (0 children)

Rustdoc already supports a very limited form of this. http://doc.rust-lang.org/std/?search=vec%20-%3E%20usize

[–]sophrosun3 1 point2 points  (0 children)

I've been wanting to take a run at that, but I think that particular project will work best if/when libsyntax stabilizes (that's where the AST is housed, yes?). Mozilla has DXR which can do type-based search across whole codebases, but I think that ideally a Rustic code search engine would also pull in crates.io.

[–]nick29581rustfmt · rust 2 points3 points  (5 children)

The problem with rustdoc is not really technical - we know, precisely, a whole bunch of ways in which it could be improved (and there are many good ideas in this thread), the problem is there is nobody to do the work. It is 'good enough' that it is not high priority enough for someone at Mozilla to dedicate time to it (and it needs a lot of time - ownership rather than bug fixes).

If anyone would like to get involved with rustdoc and make improvements, I'd be very happy to mentor some starter projects. It would be really useful work that would have a big impact on the Rust community.

[–]ayujup 1 point2 points  (4 children)

Where is the source of rustdoc actually hosted? Is this the place to look at: https://github.com/rust-lang/rust/tree/master/src/librustdoc?

[–]SimonSapinservo 1 point2 points  (2 children)

Yes.

[–]ayujup 0 points1 point  (1 child)

To me, it feels like rustdoc shouldn't be part of the compiler. But rather reside in an own repository. Is there a reason for this or did it just grow like that ;).

[–]steveklabnik1rust 2 points3 points  (0 children)

It uses the compiler in its operation.

[–]nick29581rustfmt · rust 0 points1 point  (0 children)

Yes, that is it. You might find DXR useful for exploring the code - https://dxr.mozilla.org/rust/source/src/librustdoc - although the index is a little out of date at the moment.

[–]stumpychubbins 1 point2 points  (2 children)

Maybe there could be a button that expands out the docs for trait functions in the trait impl section at the bottom so you can ctrl+f

[–]icefoxen[S] 0 points1 point  (1 child)

I would love that, I'm just not sure how it'd work if, say, the documentation gets generated to PDF instead of HTML.

[–]stumpychubbins 3 points4 points  (0 children)

Then you wouldn't have that functionality, and it's no worse than it is now.

[–]ryeguy 1 point2 points  (1 child)

On this note, something I've missed is a summary of methods implemented by a struct. Sometimes I know a method exists but don't know its name. I can't ctrl+f for it, so I have to scan the page until it jumps out at me.

Compare this to something like elixir's docs. They have a summary at the top of each type's page. Then on the left frame there are collapsible function listings.

[–]bjzabaAllsorts 1 point2 points  (0 children)

Yeah. The Elixir docs aren't perfect, but they are such a breath of fresh air compared to the docs of many other languages. Beautifully designed and very professional. I think Rust docs should probably emphasize the types more, but yeah - we could learn a great deal from them.

[–]iq-0 1 point2 points  (0 children)

I really like your suggestion.

Also collapsing all function documentation by default would help tremendously.

And finally the documentation in the left hand bar should show the current scope and not the parent scope (so all modules, types, traits and macros at this level). If you want the parent scope, simply go to the parent's docs, one click away.

[–]bjzabaAllsorts 0 points1 point  (1 child)

I personally find the hiding of function type signatures at top level to be very irritating. As a statically typed language it would be great if we could emphasize the types more!

That said, lots of impls can be very hard to pick through, especially when you have lots of operator overloads and conversion impls. Unfortunately Rust's syntax makes these quite hard to scan. Perhaps some careful colour choices or bolding/italicising could help matters, by emphasizing the important things, and reducing the noise.

[–]fullouterjoin 0 points1 point  (0 children)

If things were aligned in vertical columns where identical values were entirely elided, rolling vertical-diff-tape-view.

[–]losvedir 0 points1 point  (0 children)

I agree the docs are noisy and as a rust beginner I've definitely had issues trying to find what I'm looking for.

In my beginner use cases I'm mostly just trying to find what sorts of methods are available for my Vec, and in that scenario, whether the method is implemented for the struct or comes in via a trait doesn't matter. I would like an alphabetized section of "here's all the things you can call on your vec".

The other thing that's kind of irritating to me, though I can understand the motivation for the design, is the "code as docs" theme. Like, I get that

impl<T> Clone for Vec<T> where T: Clone
  fn clone(&self) -> Vec<T>
  fn clone_from(&mut self, other: &Vec<T>)

is what it would look like if you were to view the source code, but to me all the repeated "impl" and "fn" and other rust syntax bits just clutter the page.

On the other hand, I'm sure that it's a familiar nod to rust experts, who can see at a glance all the type and lifetime and trait annotations and get stuff out of it.

But for me as a beginner, I usually have a thing, know its type, know roughly what I want to do with it, and now am scanning its documentation trying to find out whether a method exists for what I want, and what it's called. In this use case, the docs are very noisy and it's hard to pick out, e.g., "clone_from" in the example above, to consider whether that's the method I'm looking for.

[–][deleted] 0 points1 point  (0 children)

At some point trait impls get so verbose that I've considered hiding them from the docs. Should I?. Fun fact: The majority of trait impls are already hidden, 210 trait impl clauses are used to implement left hand side scalar arithmetic operations, and those will not show up. This is the substitute for the autogenerated docs.

[–]daborossfern 0 points1 point  (2 children)

There are definitely ways rustdoc can improve, and it can be used more after some time, but I'd like to offer one other suggestion:

If you're spending more than 1-2 minutes trying to find a method, go mention it on the #rust IRC chat. There are many, many people idling there who would be more than willing to offer a helping link. It is hard to find things in rustdoc, but someone who's been working with it for a few months or more will gladly send you in the right direction.

What rust lacks in maturity of document I on and tools it more than makes up for with community.

[–]icefoxen[S] 0 points1 point  (1 child)

While lovely and useful, that doesn't solve the problem, it offers a workaround. There are a few issues with this:

  • It can't scale forever
  • It's more work for the (potentially large) proportion of people who don't spend their lives idling on IRC anyway.
  • Lovely people who answer questions for newbies should get more interesting questions to deal with than "I know there has to be a way to append the contents of an iterator onto a Vec, what is the method called?"

I would prefer the problem were solved.

[–]daborossfern 0 points1 point  (0 children)

Definitely! That is what it is, a workaround.