rust-lang / rust-lang/rust-clippy
Lint when using `format!` or `Vec::join` in `fmt::Display` and `fmt::Debug` implementations
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
Within an implementation of core::fmt::<Various>::fmt, it is usually wasteful and unintentional to use format! or Vec::from_iterator + Vec::join and other similar allocating methods.
This lint would warn on such cases to educate the author on ways to prevent allocating in the fmt method.
Similar warnings would be useful for manual implementations of serde::Serialize, but is out of scope for this warning.
Advantage
- Removes wasteful alloc/free during
Debug/Display::fmtimpacting performance.
Drawbacks
There are situations where an author may need to allocate an intermediate string before writing to the formatter such as
- when handling width and padding inputs. In such cases, this lint would be a false positive.
- or if the underlying writer for the formatter is not well suited to handle short-writes, then the suggested changes may worsen performance.
Additionally, the recommended code is admittedly harder to read.
Example
struct DisplayMySlice<'a>(&'a [ComplexObject]);
impl std::fmt::Display for DisplayMySlice<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"[{}]",
self.0
.iter()
.map(|x| format!("{x}"))
.collect::<Vec<_>>()
.join(", ")
)
}
}
Could be written as:
impl std::fmt::Display for DisplayMySlice<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut iter = self.0.iter();
if let Some(value) = iter.next() {
write!(f, "[{value}")?;
for value in iter {
write!(f, ", {value}")?;
}
write!(f, "]")
} else {
write!(f, "[]")
}
}
}
When iter::Intersperse is stabilized, the recommendation would be to cast the values to &dyn Display and use that instead:
#![feature(iter_intersperse)]
impl std::fmt::Display for DisplayMySlice<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "[")?;
for v in self
.0
.iter()
.map(|v| v as &dyn std::fmt::Display)
.intersperse(&", ")
{
write!(f, "{v}")?;
}
write!(f, "]")
}
}
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
No repository file or test is named. Start from implementations of core::fmt::::fmt and review the format!, Vec::from_iterator, and Vec::join examples; done means identifying wasteful allocations in Display and Debug formatting while excluding the stated serde::Serialize scope and accounting for documented false-positive cases.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- tooling
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100