Rust: Rustdoc doesn't handle impl section of type definitions

Created on 6 Mar 2016  路  18Comments  路  Source: rust-lang/rust

In rust-xcb I have the following code:

/// a key was pressed/released
pub type KeyPressEvent = base::Event<xcb_key_press_event_t>;

impl KeyPressEvent {
    /// The keycode (a number representing a physical key on the keyboard) of the key
    /// which was pressed.
    pub fn detail(&self) -> Keycode { ... }

    /// Time when the event was generated (in milliseconds).
    pub fn time(&self) -> Timestamp { ... }

    ...
}

However as one can see here, the impl section is ignored.

C-bug T-dev-tools T-rustdoc

Most helpful comment

This is also badly needed in nalgebra, which suffers both from otherwise-invisible impl blocks and from its type aliases ultimately resolving to complicated generic types which have a very large number of methods, most of which are not relevant to any particular alias the user might be coming from. In this case, displaying the otherwise-invisible impl blocks would be an improvement, though it wouldn't be a complete solution.

All 18 comments

Yes, they're currently ignored. I believe @alexcrichton was the one who made this change?

This was never really "added" or "changed", it's just a bug in the implementation.

@srinivasreddy No I'm not, but I'd be happy to see this fixed

@rtbo Thanks for confirmation. I am on it.

What is the expectation here?

To only show direct impls on the typedef (impl KeyPressEvent ...) or to show all impls on the type the typedef is referencing? (impl base::Event<xcb_key_press_event_t> ...)

related #19381

@mitaa my expectation would be to have on the rustdoc page of the type alias the methods documentation from the same alias.

I'd say methods from the original type that apply should be shown here, just like methods from Derefs would.

Wonder whether it's still considered a known/ignored rustdoc bug? It's a bit of a PITA for crates that defined most types as specialisations, which is a quite common part in FFI-heavy code (this and the fact that typedefs are eagerly expanded everywhere even when it doesn't make much sense).

+1 on this, https://docs.rs/xi-rope/0.2.0/xi_rope/rope/type.Rope.html is missing all the impl Rope methods, which is where so much of the good stuff is.

I am looking into this, as it makes rust-xcb quite difficult to use. It's my first time poking around in the rustdoc code, so it might take me a little while.

My initial prototype renders this code:

pub trait Trait1 {
    /// From Trait1
    fn trait_func1();
}

pub trait Trait2 {
    /// From Trait2
    fn trait_func2();
}

/// This is a MyStruct.
pub struct MyStruct;

impl MyStruct {
    /// do_stuff() with MyStruct
    pub fn do_stuff(&self) {}
}

impl Trait1 for MyStruct {
    fn trait_func1() {}
}

/// This is a MyAlias
pub type MyAlias = MyStruct;

impl MyAlias {
    /// do_more_stuff() with MyAlias
    pub fn do_more_stuff(&self) {}
}

impl Trait2 for MyAlias {
    fn trait_func2() {}
}

... like this:

image

I haven't played around with Deref to see how it interacts, but I'll do that next.

I'd missed that there was a PR (#25892) referenced from related issue #19381, which took a similar approach to the one I was preparing. That PR was closed as @alexcrichton pointed out:

I'm also not sure if we want to take this route just yet due to #14072. For example on the documentation page for io::Result the parameter E is generic instead of being wired up to Error.

You can see this in the following example:

/// A generic struct.
pub struct GenericStruct<T> {
    i: T
}

impl<T> GenericStruct<T> {
    /// A method on TypedefStruct.
    pub fn generic_impl_method(arg: T) {}
}

/// A typedef for GenericStruct, without any generics.
pub type TypedefStruct = GenericStruct<u8>;

impl TypedefStruct {
    /// A method on TypedefStruct.
    pub fn typedef_impl_method() {}
}

... which renders as:

image

In the impl for GenericStruct, it isn't obvious that the type variable T is u8.

I had a look at coming up with a solution for #14072, but it's very involved and I think it's beyond my abilities. I'll continue to take a look, but I am not optimistic!

From the closed PR #25892:

I suppose it's also somewhat unclear to me what the motivation for this is because finding the documentation of the typedef itself should just be following a link.

It is worth pointing out, that in the case of rust-xcb, it has impl blocks on type aliases, which aren't visible anywhere in the docs due to this issue. It's especially hard to discover the API, as the impl blocks are automatically generated bindings.

Would it be an improvement if the page for TypedefStruct would show only the default impl and trait impls made directly on TypedefStruct, but nothing for GenericStruct? The user can still click on the GenericStruct to get to the rest of the documentation, as they do today. (This solution isn't ideal, but it avoids making #14072 worse).

This is also badly needed in nalgebra, which suffers both from otherwise-invisible impl blocks and from its type aliases ultimately resolving to complicated generic types which have a very large number of methods, most of which are not relevant to any particular alias the user might be coming from. In this case, displaying the otherwise-invisible impl blocks would be an improvement, though it wouldn't be a complete solution.

This is also badly needed in nalgebra, which suffers both from otherwise-invisible impl blocks and from its type aliases ultimately resolving to complicated generic types which have a very large number of methods, most of which are not relevant to any particular alias the user might be coming from.

Ah, I hadn't even considered the need to recursively follow type aliases, which would be necessary for nalgebra:

type Matrix2<N> = MatrixN<N, U2>;
type MatrixN<N, D> = MatrixNM<N, D, D>;
type MatrixNM<N, R, C> = Matrix<N, R, C, MatrixArray<N, R, C>>;
pub struct Matrix<N: Scalar, R: Dim, C: Dim, S> { .. }

// Ideally this would be visible in the documentation for Matrix2<N>,
// with the correct types substituted throughout the impl:
impl<N, R: Dim, C: Dim, S> Matrix<N, R, C, S> 
where N: Scalar + ClosedNeg,
        S: StorageMut<N, R, C> { .. }

In this case, displaying the otherwise-invisible impl blocks would be an improvement, though it wouldn't be a complete solution.

I've created a pull request (#42027) for this simple improvement.

@mjkillough Just to add, in my case it looks like this:

trait ID {}
struct Object<T: ID> {}
impl<T: ID> Object<T: ID> { /* shared methods */ }

struct FileID {}
impl ID for FileID {}
pub type File = Object<FileID>;
impl File { /* file-specific methods */ }

struct GroupID {}
impl ID for GroupID {}
pub type Group = Object<GroupID>;
impl Group { /* group-specific methods */ }

trait ContainerID : ID {}
impl ContainerID for FileID {}
impl ContainerID for GroupID {}
impl<T: ContainerID> Object<T> { /* container methods */ }

This way, help for File would ideally display (1) base methods from Object, (2) methods from File, (3) shared Container methods. Currently, it displays none, of course...

(e.g. this example and this one, may also involve #14072 to some extent...)

What is the status of this issue? It has very unfortunate consequences on the rustsim family of crates (nalgebra, nphysics, etc.) which extensively use type definitions. Much of their generated documentation ends up basically useless because all methods on some type are actually implemented on a more generic version of it.

Are there any known workarounds? Mentoring instructions if someone wanted to work on this?

@fintelia Don鈥檛 think there鈥檚 any workarounds...

Was this page helpful?
0 / 5 - 0 ratings

Related issues

SharplEr picture SharplEr  路  3Comments

lambda-fairy picture lambda-fairy  路  3Comments

dtolnay picture dtolnay  路  3Comments

Robbepop picture Robbepop  路  3Comments

modsec picture modsec  路  3Comments