rust-lang / rust-lang/rust-clippy

New lint suggestion: asterisk syntax used for precision in a format specifier

Open
#14,513 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-lint
Dominant language
Rust
Stars
13.5k
Forks
2.2k
Avg merge
2d 10h
Merged PRs (30d)
32

Description

What it does

This lint warns against using an asterisk (*) as the precision in a format string. For example:

println!("{:.*}", 3, 2.0/3.0)
// prints "0.667"

The suggested alternative would be to use positional or named references:

println!("{:.1$}", 2.0/3.0, 3);
// prints "0.667"

println!("{:.prec$}", 2.0/3.0, prec=3);
// prints "0.667"
Advantage

I see a couple of issues with the asterisk syntax:

  • It implicitly takes the "next" argument from the argument list, which is surprising to anyone who hasn't seen this syntax. It also doesn't quite fit into the spirit of Rust where usually explicit is preferred over implicit IMO.
  • If the argument value is also implicit (e.g. println!("{:.*}", 3, 2.0/3.0)), the precision value is the first argument and the value to be formatted the second one. This doesn't correspond with the order of those elements in the format string.
  • I found the docs for the asterisk syntax pretty confusing, especially because there's two cases depending on the kind of reference used for the value itself. There's probably a large percentage of Rust users who don't know about this or don't understand it (like I did until recently).
  • The asterisk syntax doesn't work for the width part of the format specifier, which is inconsistent.

Fortunately, there are good alternatives. It is possible to specify both precision and width using an explicit reference to an argument. This can either be an index for the n-th argument or a named argument (see the two examples given above). Advantages of the explicit syntax:

  • Using expressively named arguments, it should be pretty obvious which arguments are being referenced, even for someone not familiar with the format specifier syntax.
  • Using positional references isn't as obvious, but still better than using the asterisk IMO.
Drawbacks
  • You could say that it's more verbose to use explicit references. I think this feature isn't used often though, so being more verbose could actually be a good thing.
  • People familiar with C's printf (which also has the asterisk syntax for taking the precision value from an argument) might be surprised that in Rust, this syntax shouldn't be used. printf is different though because it doesn't support positional or named arguments, which makes the asterisk feature easier to describe and understand.
Example
println!("The two values are {:.*} and {val:.*}", 3, 2.0/3.0, 5, val=4.0/7.0);
// prints: "The two values are 0.667 and 0.57143"

Could be written as:

println!("The two values are {:.prec1$} and {val:.prec2$}", 2.0/3.0, val=4.0/7.0, prec1=3, prec2=5);
// prints: "The two values are 0.667 and 0.57143"

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

The issue names no implementation files or tests. Start with Rust's format-string precision documentation linked in the issue and inspect Clippy's existing format-string lint entry points. Done means the lint identifies asterisk precision syntax and recommends explicit positional or named references, with coverage for the implicit and named examples shown.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
devtools
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.