Rustdoc hiding important bounds

I was playing around with some HashMap trait object silliness, so I went to the docs to find out the necessary bounds for HashMap::insert(). To my surprise, it appears that there are no bounds on K at all.

In practice, of course, there are (Hash + Eq), but the only mention of them is hidden in the middle of the page. Is there some way we could make this sort of de facto required bound more prominent, even if it's not strictly required for all methods?

7 Likes

I too would appreciate a way to keep the impl block around a list of methods more visible. I’m not deeply familiar with what is or isn’t easily possible in HTML, but I would appreciate e.g. if the

impl<K, V, S> HashMap<K, V, S>
where
    K: Eq + Hash,
    S: BuildHasher,

would stay visible at the top of the page (similar to a top menu), and scrolling would happen inside of the impl block, as long as you are inside of that impl, only disappearing e.g. once the (start of the) last item leaves the page.

E.g. for

... previous
... items
... contents
impl Foo {
    fn first_method()
    ... method
    ... documentation
    fn second_method()
    ... method
    ... documentation
    fn third_method()
    ... method
    ... documentation
}
... next
... items
... contents

a 6-line scroll area should animate roughly as follows:

... previous
... items
... contents
impl Foo {
    fn first_method()
    ... method
... items
... contents
impl Foo {
    fn first_method()
    ... method
    ... documentation
... contents
impl Foo {
    fn first_method()
    ... method
    ... documentation
    fn second_method()
impl Foo {
    fn first_method()
    ... method
    ... documentation
    fn second_method()
    ... method
impl Foo {
    ... method
    ... documentation
    fn second_method()
    ... method
    ... documentation
impl Foo {
    ... documentation
    fn second_method()
    ... method
    ... documentation
    fn third_method()
impl Foo {
    fn second_method()
    ... method
    ... documentation
    fn third_method()
    ... method
impl Foo {
    ... method
    ... documentation
    fn third_method()
    ... method
    ... documentation
impl Foo {
    ... documentation
    fn third_method()
    ... method
    ... documentation
}
impl Foo {
    fn third_method()
    ... method
    ... documentation
}
... next
    fn third_method()
    ... method
    ... documentation
}
... next
... items
    ... method
    ... documentation
}
... next
... items
... contents

(above, the impl Foo line starts disappearing as soon as the beginning of the last item, i.e. fn third_method(), has reached the top of the screen)

7 Likes

That should be possible with position: sticky.

7 Likes

I'd rather just make the generic types link to the impl block where they're defined, like this:

pub fn insert(&mut self, k: K, v: V) -> Option<V>

The problem with using a sticky header is that we don't know how tall it needs to be. If it's too tall, there won't be enough space for the scrolled content, but if it's truncated, it's the where clauses, the most important information, that will be removed.

Also, scroll-margin-top requires a specified height to make sure anchor links work correctly.

7 Likes

I think it would be good if Rustdoc normalized everything so that equivalent things show up the same way, regardless of how they are written in the source code.

In this case, the bounds would show up with each method affected rather than on the impl. I think there shouldn't be separate impl blocks in the docs for inherent methods.

In my own libraries, I often have inherent method implementations spread out in separate modules (typically with the same bounds or no bounds), which should be invisible to users. But this causes rustdoc to split the documentation into those impl blocks.

Getting rid of separate impl blocks would also allow listing inherent methods in the same alphabetical order as the appear in the navigation menu on the left.

7 Likes

:+1: for this. Whether it's an impl block with bounds or a boundless impl block with bounds on the fn doesn't really matter to the consumer. TBH I'm not sure why it seems common (at least in the standard library) to add one-item impl blocks with different bounds instead of just putting the bounds on the method.

impl blocks can be commented, which is part of why they're replicated into the the rustdoc.

Also, it can be considered a positive that things aren't in the same order. The left menu is an index, where alphabetical makes sense as a way to find things. But in the body of the page, allowing the code author to put things in a meaningful order is also a good thing -- especially when there are method variants with prefixes.

The problem with using a sticky header is that we don't know how tall it needs to be.

This is a general problem with rustdoc that it doesn't use available horizontal space and instead reformats stuff vertically by default. Which leads to this problem and others. I don't get the current design decisions behind rustdoc. Did I miss some trend where people started coding on their smartphones?

1 Like

True, but probably unusual. At least in the common case without multiple different comments it could merge these.

I can see this rationale in theory, but in practice I have found it more annoying than useful that the orders are different. If you try to scroll through the page to find something rather than clicking (which should make sense given it's all on one page) you never know which way to scroll.

1 Like

It definitely seems like adjacent uncommented impl blocks with the same bounds could be trivially merged without causing any issues.

The big thing that having the method order follow source order allows is putting all constructors at the top. I find it very useful when I'm trying to figure out how to make some type that normally I can just collapse-all then inspect the signatures of the top few associated functions.

1 Like

Take this with a grain of salt, etc., but FWIW,

It's generally considered good design to limit content width. For the same reason we wrap code at N characters, actually—we're surprisingly bad at consuming text with long lines. Most typographers recommend a body text width of ~80[1] characters[2] for legibility.

It's not just the ability to side-by-side multiple windows. But actually, I often do use rustdoc (and other reference sites) with a narrow side-by-side window, rather than a 16:9[3] window. In fact, web resources is actually why I keep my second monitor portrait; it's a much more comfortable experience to to browse any "document-like" resources with the narrower layout.

In order to take efficient advantage of the width of modern displays, you need a multiple-column layout. To a first order of approximation, though, using multiple browser windows is a very good way to utilize the space, and it's very difficult for an individual website to beat that, especially when it needs to support the narrow layout anyway.

It really differs based on how much thought the crate author put into the organization. Personally I think it's very useful that e.g. new and other construction functions are typically presented first.

If the implementation puts little thought into the documentation presentation (or is broken across multiple files, where controlling ordering between impl blocks is opaque at best[4]), though, I agree that representing the implementation happenstance isn't helpful to the documentation.

There's no silver bullet here. The current ordering is biased towards "exploratory" docs usage, where grouping related functionality and presenting it "in reading order" is beneficial. For reference usage, there's the sidebar, but it's not ideal since e.g. it lacks any signature information.

It's a hard problem to solve. Rustdoc is at the end of the day for API reference documentation, but at the same time it's also the most accurate way to get an overview of "what is this type used for," especially due to the culture of examples.

IMHO, rustdoc is kinda stuck in a local maximum. I don't know how much better it's possible to do with in-source annotation, though, and the further documentation is separated from the code the harder it is to keep it in sync.


  1. Of course, guidelines vary. What seems to be the most well cited literature review suggests 50–75 characters (roughly 30–50em) as the ideal band. ↩︎

  2. For English, anyway. I unfortunately lack the resources to find if anyone's done similar research into ideal text width for other languages. It's definitely an interesting topic, but not a flashy one. ↩︎

  3. :nerd_face: Minus the browser and OS chrome, of course. ↩︎

  4. IIRC it's based on the source order if all mod were inlined. ↩︎

3 Likes

Well that might be true for some people when reading paragraphs of text. But even for text I personally prefer reading wikipedia on fullscreen, not the mobile layout. That's mostly because all the boxes take up even more horizontal space. If they used the margins for infoboxes or to bring footnotes closer to the section referencing them like gwern's site does that would be a different story.

But the bulk of API documentation isn't continuous text it's quite structured and could benefit from a more grid-like, columnar layout that's used when there's enough screen estate. And I don't mean newspaper-style multi-column layout, I mean aligning different kinds of information horizontally.

See, there's not just the red space wasted. If we're specifically talking about where-clauses then it's also wasting the green space.

Compare to java

The current layout may be fine for people with small windows on a single screen. But it doesn't work well when you bring up documentation fullscreen on a separate monitor.

especially when it needs to support the narrow layout anyway.

Well, different layouts can be applied based on window width. The javadoc table linked above is not actually a table, it's a grid that folds in the last column on smaller widths

2 Likes

As mentioned before, I have no intuition about what is or isn’t possible with HTML/CSS easily, but I can imagine solutions to the “not enough space” problem without killing the sticky header entirely, namely

  • the sticky-header-based design could be limited to display sizes of some appropriate minimal size
  • for particularly long where clauses that don’t fit the screen size, the where clause could collapse and only be shown when expanded with a button and/or by hovering maybe; the expanded where clause could then be a temporary pop-up / overlay over the contents below

With the second idea in mind, a minimal and definitely possible easy improvement could be to offer some button to click (or hover) in order to show an overlay with (a copy of) the entire impl block and its where clause for every item in an impl block. Perhaps comparable in design to the already existing “notable traits” information. Hovering over or clicking some on-screen button seems infinitely better than the status quo of needing to scroll up an unknown distance (and afterwards find your way back down to the item).

For anyone unfamiliar with the “notable traits” information, here’s a screenshot:

Any problem can be solved by adding another button, except having too many buttons.

Could this be fixed by adding to the existing notable trait popover, and making it a general info box?

1 Like