rust-lang / rust-lang/rust

rustdoc: Show whether generic type params actually are `?Sized`, not whether definition site syntactically includes `?Sized`

Open
#143,197 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

C-enhancement T-rustdoc
Dominant language
Rust
Stars
119k
Forks
16.1k
PR merge metrics
PR metrics pending

Description

Code

?Sized is unlike other bounds: while T: Foo demands that T implement Foo, T: ?Sized allows T to not be Sized — it does not demand it. It relaxes an obligation, that's it.

If that obligation is demanded by another clause, then adding ?Sized has no effect. For example:

trait Private: Sized {}

pub fn example<T: Private + ?Sized>(value: &T) {}

Here T: Private and Private: Sized imply T: Sized, so T: ?Sized has no effect. This is confirmed by clippy, which raises a needless_maybe_sized lint here.

Since rustdoc is a tool for documenting the public API of Rust programs, I believe that it is misleading and undesirable for it to report T: ?Sized in circumstances where unsized types will not be accepted. It doesn't matter if the definition site of the generic type parameter syntactically mentions T: ?Sized if that clause has no effect on the public API.

Reproduction Steps

cargo doc for HTML output.

RUSTDOCFLAGS="-Z unstable-options --output-format=json --cap-lints=allow" cargo doc for JSON output.

Expected Outcome

Observe pub fn example<T: Private>(value: &T) as the recorded function signature in HTML docs. T: ?Sized does not apply to the API in practice, so it is excluded.

By the same reasoning, do not include T: ?Sized as a trait bound in rustdoc JSON.

Actual Output

Observe pub fn example<T: Private + ?Sized>(value: &T) as the recorded function signature in HTML docs.

Observe the following item in rustdoc JSON:

{
    "id": 0,
    "crate_id": 0,
    "name": "example",
    "span": {
        // omitted for brevity
    },
    "visibility": "public",
    "docs": null,
    "links": {},
    "attrs": [],
    "deprecation": null,
    "inner": {
        "function": {
            "sig": {
                // omitted for brevity
            },
            "generics": {
                "params": [
                    {
                        "name": "T",
                        "kind": {
                            "type": {
                                "bounds": [
                                    {
                                        "trait_bound": {
                                            "trait": {
                                                "path": "Private",
                                                "id": 1,
                                                "args": {
                                                    "angle_bracketed": {
                                                        "args": [],
                                                        "constraints": []
                                                    }
                                                }
                                            },
                                            "generic_params": [],
                                            "modifier": "none"
                                        }
                                    },
                                    {
                                        "trait_bound": {
                                            "trait": {
                                                "path": "Sized",
                                                "id": 2,
                                                "args": {
                                                    "angle_bracketed": {
                                                        "args": [],
                                                        "constraints": []
                                                    }
                                                }
                                            },
                                            "generic_params": [],
                                            "modifier": "maybe"  // <-- `?Sized`
                                        }
                                    }
                                ],
                                "default": null,
                                "is_synthetic": false
                            }
                        }
                    }
                ],
                "where_predicates": []
            },
            // omitted for brevity
        }
    }
}

Version

rustc 1.90.0-nightly (28f1c8079 2025-06-24)
binary: rustc
commit-hash: 28f1c807911c63f08d98e7b468cfcf15a441e34b
commit-date: 2025-06-24
host: x86_64-unknown-linux-gnu
release: 1.90.0-nightly
LLVM version: 20.1.7

@rustbot label +A-rustdoc-json

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the issue with cargo doc and with RUSTDOCFLAGS="-Z unstable-options --output-format=json --cap-lints=allow" cargo doc, then trace rustdoc's generic-bound handling for HTML and JSON output. Done means an implied Sized obligation causes ?Sized to be omitted from both the rendered signature and rustdoc JSON.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
compilers, documentation
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.