rust-lang / rust-lang/rust-clippy
Lint on explicit format specifiers in manual Debug implementation
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 13.5k
- Forks
- 2.2k
- Avg merge
- 2d 10h
- Merged PRs (30d)
- 32
Description
What it does
When implementing std::fmt::Debug by hand, using {:?} or {:#?} for inner fields is usually an unwitting mistake where the author doesn't realise that an explicit format specifier will override user's formatting preferences instead of propagating them unchanged.
Lint Name
manual-fmt-in-debug
Category
correctness, suspicious
Advantage
It will improve custom Debug implementations across the ecosystem to better work with each other, propagating user's preferences throughout types.
Drawbacks
Sometimes user intentionally wants to use compact format for nested fields - e.g. if a custom structure is pretty-printed, but contains raw blob of bytes they want to be printed on one line.
In those cases, user should be able to opt-out via #[allow(...)], but the gut feeling is that those are relatively rare compared to compact format used unwittingly.
Example
#[derive(Debug, Clone, Copy)]
struct A {
x: u32,
y: u32,
}
struct BadB(A);
impl std::fmt::Debug for BadB {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "<<<{:?}>>>", &self.0) // this should trigger the lint
}
}
fn main() {
let a = A {
x: 10,
y: 20
};
println!("{:#?}", BadB(a));
println!("{:#X?}", BadB(a));
}
Output:
<<<A { x: 10, y: 20 }>>>
<<<A { x: 10, y: 20 }>>>
Could be written as:
#[derive(Debug, Clone, Copy)]
struct A {
x: u32,
y: u32,
}
struct GoodB(A);
impl std::fmt::Debug for GoodB {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("<<<")?;
self.0.fmt(f)?;
f.write_str(">>>")?;
Ok(())
}
}
fn main() {
let a = A {
x: 10,
y: 20
};
println!("{:#?}", GoodB(a));
println!("{:#X?}", GoodB(a));
}
Output:
<<<A {
x: 10,
y: 20,
}>>>
<<<A {
x: 0xA,
y: 0x14,
}>>>
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
Begin with the manual-fmt-in-debug requirements and compare the BadB and GoodB Rust examples. Done means explicit {:?} and {:#?} specifiers in manual Debug implementations are diagnosed, while intentionally compact nested formatting can be allowed as described.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- devtools
- Issue type
- Feature
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100