Skip to content

Commit 75716b4

Browse files
committed
Auto merge of #118159 - EliasHolzmann:formatting_options, r=m-ou-se
Implementation of `fmt::FormattingOptions` Tracking issue: #118117 Public API: ```rust #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct FormattingOptions { … } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Sign { Plus, Minus } #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum DebugAsHex { Lower, Upper } impl FormattingOptions { pub fn new() -> Self; pub fn sign(&mut self, sign: Option<Sign>) -> &mut Self; pub fn sign_aware_zero_pad(&mut self, sign_aware_zero_pad: bool) -> &mut Self; pub fn alternate(&mut self, alternate: bool) -> &mut Self; pub fn fill(&mut self, fill: char) -> &mut Self; pub fn align(&mut self, alignment: Option<Alignment>) -> &mut Self; pub fn width(&mut self, width: Option<usize>) -> &mut Self; pub fn precision(&mut self, precision: Option<usize>) -> &mut Self; pub fn debug_as_hex(&mut self, debug_as_hex: Option<DebugAsHex>) -> &mut Self; pub fn get_sign(&self) -> Option<Sign>; pub fn get_sign_aware_zero_pad(&self) -> bool; pub fn get_alternate(&self) -> bool; pub fn get_fill(&self) -> char; pub fn get_align(&self) -> Option<Alignment>; pub fn get_width(&self) -> Option<usize>; pub fn get_precision(&self) -> Option<usize>; pub fn get_debug_as_hex(&self) -> Option<DebugAsHex>; pub fn create_formatter<'a>(self, write: &'a mut (dyn Write + 'a)) -> Formatter<'a>; } impl<'a> Formatter<'a> { pub fn new(write: &'a mut (dyn Write + 'a), options: FormattingOptions) -> Self; pub fn with_options<'b>(&'b mut self, options: FormattingOptions) -> Formatter<'b>; pub fn sign(&self) -> Option<Sign>; pub fn options(&self) -> FormattingOptions; } ``` Relevant changes from the public API in the tracking issue (I'm leaving out some stuff I consider obvious mistakes, like missing `#[derive(..)]`s and `pub` specifiers): - `enum DebugAsHex`/`FormattingOptions::debug_as_hex`/`FormattingOptions::get_debug_as_hex`: To support `{:x?}` as well as `{:X?}`. I had completely missed these options in the ACP. I'm open for any and all bikeshedding, not married to the name. - `fill`/`get_fill` now takes/returns `char` instead of `Option<char>`. This simply mirrors what `Formatter::fill` returns (with default being `' '`). - Changed `zero_pad`/`get_zero_pad` to `sign_aware_zero_pad`/`get_sign_aware_zero_pad`. This also mirrors `Formatter::sign_aware_zero_pad`. While I'm not a fan of this quite verbose name, I do believe that having the interface of `Formatter` and `FormattingOptions` be compatible is more important. - For the same reason, renamed `alignment`/`get_alignment` to `aling`/`get_align`. - Deviating from my initial idea, `Formatter::with_options` returns a `Formatter` which has the lifetime of the `self` reference as its generic lifetime parameter (in the original API spec, the generic lifetime of the returned `Formatter` was the generic lifetime used by `self` instead). Otherwise, one could construct two `Formatter`s that both mutably borrow the same underlying buffer, which would be unsound. This solution still has performance benefits over simply using `Formatter::new`, so I believe it is worthwhile to keep this method.
2 parents 8dc8377 + 31a5657 commit 75716b4

11 files changed

+374
-93
lines changed

library/alloc/src/fmt.rs

+2
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,8 @@ pub use core::fmt::{Arguments, write};
596596
pub use core::fmt::{Binary, Octal};
597597
#[stable(feature = "rust1", since = "1.0.0")]
598598
pub use core::fmt::{Debug, Display};
599+
#[unstable(feature = "formatting_options", issue = "118117")]
600+
pub use core::fmt::{DebugAsHex, FormattingOptions, Sign};
599601
#[stable(feature = "rust1", since = "1.0.0")]
600602
pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple};
601603
#[stable(feature = "rust1", since = "1.0.0")]

library/alloc/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,7 @@
117117
#![feature(extend_one_unchecked)]
118118
#![feature(fmt_internals)]
119119
#![feature(fn_traits)]
120+
#![feature(formatting_options)]
120121
#![feature(hasher_prefixfree_extras)]
121122
#![feature(inplace_iteration)]
122123
#![feature(iter_advance_by)]

library/alloc/src/string.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -2682,7 +2682,8 @@ impl<T: fmt::Display + ?Sized> ToString for T {
26822682
#[inline]
26832683
default fn to_string(&self) -> String {
26842684
let mut buf = String::new();
2685-
let mut formatter = core::fmt::Formatter::new(&mut buf);
2685+
let mut formatter =
2686+
core::fmt::Formatter::new(&mut buf, core::fmt::FormattingOptions::new());
26862687
// Bypass format_args!() to avoid write_str with zero-length strs
26872688
fmt::Display::fmt(self, &mut formatter)
26882689
.expect("a Display implementation returned an error unexpectedly");

library/core/src/fmt/float.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ where
8686
true => flt2dec::Sign::MinusPlus,
8787
};
8888

89-
if let Some(precision) = fmt.precision {
89+
if let Some(precision) = fmt.options.precision {
9090
float_to_decimal_common_exact(fmt, num, sign, precision)
9191
} else {
9292
let min_precision = 0;
@@ -162,7 +162,7 @@ where
162162
true => flt2dec::Sign::MinusPlus,
163163
};
164164

165-
if let Some(precision) = fmt.precision {
165+
if let Some(precision) = fmt.options.precision {
166166
// 1 integral digit + `precision` fractional digits = `precision + 1` total digits
167167
float_to_exponential_common_exact(fmt, num, sign, precision + 1, upper)
168168
} else {
@@ -180,7 +180,7 @@ where
180180
true => flt2dec::Sign::MinusPlus,
181181
};
182182

183-
if let Some(precision) = fmt.precision {
183+
if let Some(precision) = fmt.options.precision {
184184
// this behavior of {:.PREC?} predates exponential formatting for {:?}
185185
float_to_decimal_common_exact(fmt, num, sign, precision)
186186
} else {

0 commit comments

Comments
 (0)