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/BTreeSet of 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
Consider this MRE:
After creating a new library crate for it, running
cargo doc --no-depstakes ~2 minutes on my machine. Removing the blanket implimpl<T> Erasable for T where T: Sync {}reduces the time to 0.5 seconds.The problem seems to be caused by a combination of:
SyncComputeScalar->Dependency->ComputeScalar)BTreeMap/BTreeSetof other structsRemoving 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:Also tried on nightly, with the same results: