From ede39aeb331bf6efb3739d22a60c1844e9c2c3d6 Mon Sep 17 00:00:00 2001 From: Evgeny Safronov Date: Wed, 29 Jun 2016 10:40:25 +0300 Subject: [PATCH] feat: reinterpret `precision` field for strings This commit changes the behavior of formatting string arguments with both width and precision fields set. Documentation says that the `width` field is the "minimum width" that the format should take up. If the value's string does not fill up this many characters, then the padding specified by fill/alignment will be used to take up the required space. This is true for all formatted types except string, which is truncated down to `precision` number of chars and then all of `fill`, `align` and `width` fields are completely ignored. For example: `format!("{:/^10.8}", "1234567890);` emits "12345678". In the contrast Python version works as the expected: ```python >>> '{:/^10.8}'.format('1234567890') '/12345678/' ``` This commit gives back the `Python` behavior by changing the `precision` field meaning to the truncation and nothing more. The result string *will* be prepended/appended up to the `width` field with the proper `fill` char. However, this is the breaking change. Also updated `std::fmt` docs about string precision. Signed-off-by: Evgeny Safronov --- src/libcollections/fmt.rs | 6 ++++-- src/libcore/fmt/mod.rs | 18 +++++++++++------- src/test/run-pass/ifmt.rs | 3 ++- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/src/libcollections/fmt.rs b/src/libcollections/fmt.rs index 6f77d79ab0bfe..356bb96741e4f 100644 --- a/src/libcollections/fmt.rs +++ b/src/libcollections/fmt.rs @@ -408,8 +408,8 @@ //! ## Precision //! //! For non-numeric types, this can be considered a "maximum width". If the resulting string is -//! longer than this width, then it is truncated down to this many characters and only those are -//! emitted. +//! longer than this width, then it is truncated down to this many characters and that truncated +//! value is emitted with proper `fill`, `alignment` and `width` if those parameters are set. //! //! For integral types, this is ignored. //! @@ -469,6 +469,7 @@ //! ``` //! println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56); //! println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56"); +//! println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56"); //! ``` //! //! print two significantly different things: @@ -476,6 +477,7 @@ //! ```text //! Hello, `1234.560` has 3 fractional digits //! Hello, `123` has 3 characters +//! Hello, ` 123` has 3 right-aligned characters //! ``` //! //! # Escaping diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 4ac134c2b59c8..a36f7e42c9c47 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -983,15 +983,19 @@ impl<'a> Formatter<'a> { return self.buf.write_str(s); } // The `precision` field can be interpreted as a `max-width` for the - // string being formatted - if let Some(max) = self.precision { - // If there's a maximum width and our string is longer than - // that, then we must always have truncation. This is the only - // case where the maximum length will matter. + // string being formatted. + let s = if let Some(max) = self.precision { + // If our string is longer that the precision, then we must have + // truncation. However other flags like `fill`, `width` and `align` + // must act as always. if let Some((i, _)) = s.char_indices().skip(max).next() { - return self.buf.write_str(&s[..i]) + &s[..i] + } else { + &s } - } + } else { + &s + }; // The `width` field is more of a `min-width` parameter at this point. match self.width { // If we're under the maximum length, and there's no minimum length diff --git a/src/test/run-pass/ifmt.rs b/src/test/run-pass/ifmt.rs index 27cafeacc203d..1cc52c930344c 100644 --- a/src/test/run-pass/ifmt.rs +++ b/src/test/run-pass/ifmt.rs @@ -125,7 +125,7 @@ pub fn main() { t!(format!("{:<4.4}", "aaaaaaaaaaaaaaaaaa"), "aaaa"); t!(format!("{:>4.4}", "aaaaaaaaaaaaaaaaaa"), "aaaa"); t!(format!("{:^4.4}", "aaaaaaaaaaaaaaaaaa"), "aaaa"); - t!(format!("{:>10.4}", "aaaaaaaaaaaaaaaaaa"), "aaaa"); + t!(format!("{:>10.4}", "aaaaaaaaaaaaaaaaaa"), " aaaa"); t!(format!("{:2.4}", "aaaaa"), "aaaa"); t!(format!("{:2.4}", "aaaa"), "aaaa"); t!(format!("{:2.4}", "aaa"), "aaa"); @@ -140,6 +140,7 @@ pub fn main() { t!(format!("{:a$}", "a", a=4), "a "); t!(format!("{:-#}", "a"), "a"); t!(format!("{:+#}", "a"), "a"); + t!(format!("{:/^10.8}", "1234567890"), "/12345678/"); // Some float stuff t!(format!("{:}", 1.0f32), "1");