`rustdoc` extreme slowdown with cyclic generic structures
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 119k
- Forks
- 16.1k
- PR merge metrics
- PR metrics pending
Description
Consider this MRE:
#![no_std]
extern crate alloc;
use alloc::{
collections::{BTreeMap, BTreeSet},
string::String,
sync::Arc,
};
pub trait Erasable: Sync {}
impl<T> Erasable for T where T: Sync {}
pub trait SessionParameters {
type Verifier;
}
pub struct Node<T>(pub Arc<T>);
pub struct ComputeScalar<SP: SessionParameters> {
pub args: BTreeMap<String, ComputeScalarArg<SP>>,
pub dependencies: Dependency<SP>,
}
pub struct Collect<SP: SessionParameters> {
pub values: CollectArg<SP>,
pub dependencies: Dependency<SP>,
}
pub struct ComputeMapping<SP: SessionParameters> {
pub args: BTreeMap<String, ComputeMappingArg<SP>>,
pub dependencies: Dependency<SP>,
}
pub struct SerializeAndSignBC<SP: SessionParameters> {
pub data: Node<ComputeScalar<SP>>,
pub dependencies: Dependency<SP>,
}
pub struct SerializeAndSignDM<SP: SessionParameters> {
pub data: DirectMessageArg<SP>,
pub dependencies: Dependency<SP>,
}
pub struct DeserializeAndCheck<SP: SessionParameters> {
pub data: Node<Receive<SP>>,
pub dependencies: Dependency<SP>,
}
pub struct SendDM<SP: SessionParameters> {
pub data: Node<SerializeAndSignDM<SP>>,
pub dependencies: Dependency<SP>,
}
pub struct SendBC<SP: SessionParameters> {
pub data: Node<SerializeAndSignBC<SP>>,
pub destinations: BTreeSet<SP::Verifier>,
pub dependencies: Dependency<SP>,
}
pub struct Receive<SP: SessionParameters> {
pub dependencies: Dependency<SP>,
}
pub struct MergeScalars<SP: SessionParameters> {
pub left: ComputeScalarArg<SP>,
pub right: ComputeScalarArg<SP>,
}
pub enum ComputeScalarArg<SP: SessionParameters> {
ComputeScalar(Node<ComputeScalar<SP>>),
MergeScalars(Node<MergeScalars<SP>>),
Collect(Node<Collect<SP>>),
}
pub enum ComputeMappingArg<SP: SessionParameters> {
ComputeScalar(Node<ComputeScalar<SP>>),
MergeScalars(Node<MergeScalars<SP>>),
Collect(Node<Collect<SP>>),
ComputeMapping(Node<ComputeMapping<SP>>),
SerializeAndSignBC(Node<SerializeAndSignBC<SP>>),
SerializeAndSignDM(Node<SerializeAndSignDM<SP>>),
DeserializeAndCheck(Node<DeserializeAndCheck<SP>>),
}
pub enum CollectArg<SP: SessionParameters> {
ComputeMapping(Node<ComputeMapping<SP>>),
SerializeAndSign(Node<SerializeAndSignDM<SP>>),
DeserializeAndCheck(Node<DeserializeAndCheck<SP>>),
Send(Node<SendDM<SP>>),
Receive(Node<Receive<SP>>),
}
pub enum DirectMessageArg<SP: SessionParameters> {
ComputeScalar(Node<ComputeScalar<SP>>),
ComputeMapping(Node<ComputeMapping<SP>>),
DeserializeAndCheck(Node<DeserializeAndCheck<SP>>),
}
pub enum Dependency<SP: SessionParameters> {
ComputeScalar(Node<ComputeScalar<SP>>),
Collect(Node<Collect<SP>>),
MergeScalars(Node<MergeScalars<SP>>),
SendBC(Node<SendBC<SP>>),
}
After creating a new library crate for it, running cargo doc --no-deps takes ~2 minutes on my machine. Removing the blanket impl impl<T> Erasable for T where T: Sync {} reduces the time to 0.5 seconds.
The problem seems to be caused by a combination of:
- a public trait with a blanket impl
- the trait is bound on
Sync - closed loops of connections of structures to their fields (e.g.
ComputeScalar->Dependency->ComputeScalar) - structures being generic
- the trait that's the bound for that generic type having an associated type, and that type being used in some structs
- some fields being
BTreeMap/BTreeSetof other structs
Removing any one of these either removes the problem completely, or reduces the documentation generation time dramatically (e.g. from 2 minutes to 10 seconds, which is still disproportionately much, of course).
Meta
rustc --version --verbose:
rustc 1.97.1 (8bab26f4f 2026-07-14)
binary: rustc
commit-hash: 8bab26f4f68e0e26f0bb7960be334d5b520ea452
commit-date: 2026-07-14
host: aarch64-apple-darwin
release: 1.97.1
LLVM version: 22.1.6
Also tried on nightly, with the same results:
rustc 1.99.0-nightly (dc3f85158 2026-07-26)
binary: rustc
commit-hash: dc3f85158a955a87a6e4363af1fbe9cf2d063cce
commit-date: 2026-07-26
host: aarch64-apple-darwin
release: 1.99.0-nightly
LLVM version: 22.1.8
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Use the supplied MRE in a new library crate and run cargo doc --no-deps to establish the slowdown. Compare the result with the blanket Erasable implementation removed, then trace the rustdoc path responsible for processing the cyclic generic structures. Done means a focused regression test reproduces the case and the excessive documentation-generation time is addressed.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- compilers, performance
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100