Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

std::fmt: Precision via asterisk does not fit documentation when using named or explicit positional arguments #96413

Closed
EliasHolzmann opened this issue Apr 25, 2022 · 0 comments · Fixed by #96568
Labels
C-bug Category: This is a bug.

Comments

@EliasHolzmann
Copy link
Contributor

From the documentation on std::fmt, section "Precision" (highlighting in bold by me):

There are three possible ways to specify the desired precision:

[...]

  1. An asterisk .*:

    .* means that this {...} is associated with two* format inputs rather than one: the first input holds the usize precision, and the second holds the value to print. Note that in this case, if one uses the format string {<arg>:<spec>.*}, then the <arg> part refers to the value to print, and the precision must come in the input preceding <arg>.

The bold part does not fit the current behavior. Consider the following snippet:

fn main() {
    println!("{2:.*} {}", "foobar", 1, 2.0);
}

Playground link

According to the documentation, one would expect this program to output 2.0 foobar. Instead, rustc fails to compile it:

error[[E0308]](https://doc.rust-lang.org/nightly/error-index.html#E0308): mismatched types
   [--> src/main.rs:2:26
](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021#)    |
2   |     println!("{2:.*}{}", "foobar", 1, 2.0);
    |     ---------------------^^^^^^^^---------
    |     |                    |
    |     |                    expected `usize`, found `&str`
    |     arguments to this function are incorrect
    |
    = note: expected reference `&usize`
               found reference `&&str`
note: associated function defined here
    = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)

After glancing at the implementation, it looks to me like in the asterisk case, the precision is always the next implicit positional argument:

if let Some(end) = self.consume_pos('*') {
// Resolve `CountIsNextParam`.
// We can do this immediately as `position` is resolved later.
let i = self.curarg;
self.curarg += 1;
spec.precision = CountIsParam(i);
spec.precision_span =
Some(self.to_span_index(start).to(self.to_span_index(end + 1)));
} else {

The current implementation determines the argument that will be formatted only after the argument containing the precision. This is the reason why the documentation fits the behavior for implicit positional parameters (like {:.*}). However, this doesn't help in the case of explicit positional parameters (like {42:.*}) and named parameters (like {foobar:.*}) – the precision usually does not come in the input preceding <arg>, as stated by the documentation.

In my opinion, this is not an implementation bug, but a documentation bug – the behavior hasn't changed since at least Rust 1.0, see https://godbolt.org/z/xf61oP6hE. It would probably be unwise to change it now as some crates may depend on it. I'd be happy to open a pull request fixing the documentation – there are a few other things I want to enhance in the std::fmt documentation anyway.

Meta

rustc --version --verbose:

rustc 1.62.0-nightly (18f314e70 2022-04-24)
binary: rustc
commit-hash: 18f314e7027fe7084aaab8620c624a0d7bd29e70
commit-date: 2022-04-24
host: x86_64-unknown-linux-gnu
release: 1.62.0-nightly
LLVM version: 14.0.1
@EliasHolzmann EliasHolzmann added the C-bug Category: This is a bug. label Apr 25, 2022
JohnTitor added a commit to JohnTitor/rust that referenced this issue May 2, 2022
…triplett

std::fmt: Various fixes and improvements to documentation

This PR contains the following changes:

- **Added argument index comments to examples for specifying precision**

  The examples for specifying the precision have comments explaining which
  argument the specifier is referring to. However, for implicit positional
  arguments, the examples simply refer to "next arg". To simplify following the
  comments, "next arg" was supplemented with the actual resulting argument index.

- **Fixed documentation for specifying precision via `.*`**

  The documentation stated that in case of the syntax `{<arg>:<spec>.*}`, "the
  `<arg>` part refers to the value to print, and the precision must come in the
  input preceding `<arg>`". This is not correct: the <arg> part does indeed refer
  to the value to print, but the precision does not come in the input preciding
  arg, but in the next implicit input (as if specified with {}).

  Fixes rust-lang#96413.

- **Fix the grammar documentation**

  According to the grammar documented, the format specifier `{: }` should not be
  legal because of the whitespace it contains. However, in reality, this is
  perfectly fine because the actual implementation allows spaces before the
  closing brace. Fixes rust-lang#71088.

  Also, the exact meaning of most of the terminal symbols was not specified, for
  example the meaning of `identifier`.

- **Removed reference to Formatter::buf and other private fields**

  Formatter::buf is not a public field and therefore isn't very helpful in user-
  facing documentation. Also, the other public fields of Formatter were removed
  during stabilization of std::fmt (4af3494) and can only be accessed via
  getters.

- **Improved list of formatting macros**

  Two improvements:
  1. write! can not only receive a `io::Write`, but also a `fmt::Write` as first argument.
  2. The description texts now contain links to the actual macros for easier
     navigation.
@bors bors closed this as completed in 1288883 May 2, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-bug Category: This is a bug.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant