Skip to content

Commit 3bd291a

Browse files
committed
Auto merge of rust-lang#136862 - joboet:formatter_options_ref, r=<try>
core: use `FormattingOptions` by reference Most of the time, the options passed to `Formatter` will be the unmodified default options. The current approach of using the options by-value means that a new `FormattingOptions` structure has to be created every time a formatter is constructed. With this PR, `Formatter` stores the options by-reference, which means that one `FormattingOptions` instance in static memory is enough to create a `Formatter`. In the future (and in particular once rust-lang#119899 has merged), we might even consider removing `fmt::rt::Placeholder` in favour of `FormattingOptions`, which would – in combination with this change – decrease the option-copying even further. CC rust-lang#118117 `@EliasHolzmann`
2 parents 69482e8 + cffe20d commit 3bd291a

5 files changed

+83
-62
lines changed

library/alloc/src/string.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -2751,7 +2751,7 @@ impl<T: fmt::Display + ?Sized> SpecToString for T {
27512751
default fn spec_to_string(&self) -> String {
27522752
let mut buf = String::new();
27532753
let mut formatter =
2754-
core::fmt::Formatter::new(&mut buf, core::fmt::FormattingOptions::new());
2754+
core::fmt::Formatter::new(&mut buf, const { &core::fmt::FormattingOptions::new() });
27552755
// Bypass format_args!() to avoid write_str with zero-length strs
27562756
fmt::Display::fmt(self, &mut formatter)
27572757
.expect("a Display implementation returned an error unexpectedly");

library/core/src/fmt/mod.rs

+61-56
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ impl FormattingOptions {
482482
///
483483
/// You may alternatively use [`Formatter::new()`].
484484
#[unstable(feature = "formatting_options", issue = "118117")]
485-
pub fn create_formatter<'a>(self, write: &'a mut (dyn Write + 'a)) -> Formatter<'a> {
485+
pub fn create_formatter<'a>(&'a self, write: &'a mut (dyn Write + 'a)) -> Formatter<'a> {
486486
Formatter { options: self, buf: write }
487487
}
488488

@@ -530,9 +530,8 @@ impl Default for FormattingOptions {
530530
#[stable(feature = "rust1", since = "1.0.0")]
531531
#[rustc_diagnostic_item = "Formatter"]
532532
pub struct Formatter<'a> {
533-
options: FormattingOptions,
534-
535533
buf: &'a mut (dyn Write + 'a),
534+
options: &'a FormattingOptions,
536535
}
537536

538537
impl<'a> Formatter<'a> {
@@ -544,14 +543,21 @@ impl<'a> Formatter<'a> {
544543
///
545544
/// You may alternatively use [`FormattingOptions::create_formatter()`].
546545
#[unstable(feature = "formatting_options", issue = "118117")]
547-
pub fn new(write: &'a mut (dyn Write + 'a), options: FormattingOptions) -> Self {
548-
Formatter { options, buf: write }
546+
pub fn new(write: &'a mut (dyn Write + 'a), options: &'a FormattingOptions) -> Self {
547+
Formatter { buf: write, options }
548+
}
549+
550+
fn reborrow(&mut self) -> Formatter<'_> {
551+
Formatter { buf: self.buf, options: self.options }
549552
}
550553

551554
/// Creates a new formatter based on this one with given [`FormattingOptions`].
552555
#[unstable(feature = "formatting_options", issue = "118117")]
553-
pub fn with_options<'b>(&'b mut self, options: FormattingOptions) -> Formatter<'b> {
554-
Formatter { options, buf: self.buf }
556+
pub fn with_options<'f, 'o>(&'f mut self, options: &'o FormattingOptions) -> Formatter<'f>
557+
where
558+
'o: 'f,
559+
{
560+
Formatter { buf: self.buf, options }
555561
}
556562
}
557563

@@ -1429,7 +1435,7 @@ pub trait UpperExp {
14291435
/// [`write!`]: crate::write!
14301436
#[stable(feature = "rust1", since = "1.0.0")]
14311437
pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
1432-
let mut formatter = Formatter::new(output, FormattingOptions::new());
1438+
let mut formatter = Formatter::new(output, const { &FormattingOptions::new() });
14331439
let mut idx = 0;
14341440

14351441
match args.fmt {
@@ -1478,15 +1484,17 @@ pub fn write(output: &mut dyn Write, args: Arguments<'_>) -> Result {
14781484
}
14791485

14801486
unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argument<'_>]) -> Result {
1481-
fmt.options.fill = arg.fill;
1482-
fmt.options.align = arg.align.into();
1483-
fmt.options.flags = arg.flags;
1484-
// SAFETY: arg and args come from the same Arguments,
1485-
// which guarantees the indexes are always within bounds.
1486-
unsafe {
1487-
fmt.options.width = getcount(args, &arg.width);
1488-
fmt.options.precision = getcount(args, &arg.precision);
1489-
}
1487+
let options = FormattingOptions {
1488+
flags: arg.flags,
1489+
fill: arg.fill,
1490+
align: arg.align.into(),
1491+
// SAFETY: arg and args come from the same Arguments,
1492+
// which guarantees the indexes are always within bounds.
1493+
width: unsafe { getcount(args, &arg.width) },
1494+
// SAFETY: likewise.
1495+
precision: unsafe { getcount(args, &arg.precision) },
1496+
};
1497+
let mut fmt = fmt.with_options(&options);
14901498

14911499
// Extract the correct argument
14921500
debug_assert!(arg.position < args.len());
@@ -1496,7 +1504,7 @@ unsafe fn run(fmt: &mut Formatter<'_>, arg: &rt::Placeholder, args: &[rt::Argume
14961504

14971505
// Then actually do some printing
14981506
// SAFETY: this is a placeholder argument.
1499-
unsafe { value.fmt(fmt) }
1507+
unsafe { value.fmt(&mut fmt) }
15001508
}
15011509

15021510
unsafe fn getcount(args: &[rt::Argument<'_>], cnt: &rt::Count) -> Option<usize> {
@@ -1641,16 +1649,16 @@ impl<'a> Formatter<'a> {
16411649
// The sign and prefix goes before the padding if the fill character
16421650
// is zero
16431651
Some(min) if self.sign_aware_zero_pad() => {
1644-
let old_fill = crate::mem::replace(&mut self.options.fill, '0');
1645-
let old_align =
1646-
crate::mem::replace(&mut self.options.align, Some(Alignment::Right));
1647-
write_prefix(self, sign, prefix)?;
1648-
let post_padding = self.padding(min - width, Alignment::Right)?;
1649-
self.buf.write_str(buf)?;
1650-
post_padding.write(self)?;
1651-
self.options.fill = old_fill;
1652-
self.options.align = old_align;
1653-
Ok(())
1652+
let options = FormattingOptions {
1653+
fill: '0',
1654+
align: Some(Alignment::Right),
1655+
..self.options().clone()
1656+
};
1657+
let mut f = self.with_options(&options);
1658+
write_prefix(&mut f, sign, prefix)?;
1659+
let post_padding = f.padding(min - width, Alignment::Right)?;
1660+
f.write_str(buf)?;
1661+
post_padding.write(&mut f)
16541662
}
16551663
// Otherwise, the sign and prefix goes after the padding
16561664
Some(min) => {
@@ -1776,37 +1784,40 @@ impl<'a> Formatter<'a> {
17761784
// for the sign-aware zero padding, we render the sign first and
17771785
// behave as if we had no sign from the beginning.
17781786
let mut formatted = formatted.clone();
1779-
let old_fill = self.options.fill;
1780-
let old_align = self.options.align;
1781-
if self.sign_aware_zero_pad() {
1787+
1788+
let options;
1789+
let mut f = if self.sign_aware_zero_pad() {
17821790
// a sign always goes first
17831791
let sign = formatted.sign;
17841792
self.buf.write_str(sign)?;
17851793

17861794
// remove the sign from the formatted parts
17871795
formatted.sign = "";
17881796
width = width.saturating_sub(sign.len());
1789-
self.options.fill = '0';
1790-
self.options.align = Some(Alignment::Right);
1791-
}
1797+
options = FormattingOptions {
1798+
fill: '0',
1799+
align: Some(Alignment::Right),
1800+
..self.options().clone()
1801+
};
1802+
self.with_options(&options)
1803+
} else {
1804+
self.reborrow()
1805+
};
17921806

17931807
// remaining parts go through the ordinary padding process.
17941808
let len = formatted.len();
1795-
let ret = if width <= len {
1809+
if width <= len {
17961810
// no padding
17971811
// SAFETY: Per the precondition.
1798-
unsafe { self.write_formatted_parts(&formatted) }
1812+
unsafe { f.write_formatted_parts(&formatted) }
17991813
} else {
1800-
let post_padding = self.padding(width - len, Alignment::Right)?;
1814+
let post_padding = f.padding(width - len, Alignment::Right)?;
18011815
// SAFETY: Per the precondition.
18021816
unsafe {
1803-
self.write_formatted_parts(&formatted)?;
1817+
f.write_formatted_parts(&formatted)?;
18041818
}
1805-
post_padding.write(self)
1806-
};
1807-
self.options.fill = old_fill;
1808-
self.options.align = old_align;
1809-
ret
1819+
post_padding.write(&mut f)
1820+
}
18101821
} else {
18111822
// this is the common case and we take a shortcut
18121823
// SAFETY: Per the precondition.
@@ -2610,7 +2621,7 @@ impl<'a> Formatter<'a> {
26102621

26112622
/// Returns the formatting options this formatter corresponds to.
26122623
#[unstable(feature = "formatting_options", issue = "118117")]
2613-
pub const fn options(&self) -> FormattingOptions {
2624+
pub const fn options(&self) -> &FormattingOptions {
26142625
self.options
26152626
}
26162627
}
@@ -2789,28 +2800,22 @@ impl<T: ?Sized> Pointer for *const T {
27892800
///
27902801
/// [problematic]: https://github.com/rust-lang/rust/issues/95489
27912802
pub(crate) fn pointer_fmt_inner(ptr_addr: usize, f: &mut Formatter<'_>) -> Result {
2792-
let old_width = f.options.width;
2793-
let old_flags = f.options.flags;
2803+
let mut options = f.options().clone();
27942804

27952805
// The alternate flag is already treated by LowerHex as being special-
27962806
// it denotes whether to prefix with 0x. We use it to work out whether
27972807
// or not to zero extend, and then unconditionally set it to get the
27982808
// prefix.
27992809
if f.alternate() {
2800-
f.options.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32);
2810+
options.flags |= 1 << (rt::Flag::SignAwareZeroPad as u32);
28012811

2802-
if f.options.width.is_none() {
2803-
f.options.width = Some((usize::BITS / 4) as usize + 2);
2812+
if options.width.is_none() {
2813+
options.width = Some((usize::BITS / 4) as usize + 2);
28042814
}
28052815
}
2806-
f.options.flags |= 1 << (rt::Flag::Alternate as u32);
2807-
2808-
let ret = LowerHex::fmt(&ptr_addr, f);
2809-
2810-
f.options.width = old_width;
2811-
f.options.flags = old_flags;
2816+
options.flags |= 1 << (rt::Flag::Alternate as u32);
28122817

2813-
ret
2818+
LowerHex::fmt(&ptr_addr, &mut f.with_options(&options))
28142819
}
28152820

28162821
#[stable(feature = "rust1", since = "1.0.0")]

library/std/src/panicking.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ pub fn begin_panic_handler(info: &core::panic::PanicInfo<'_>) -> ! {
637637
// Lazily, the first time this gets called, run the actual string formatting.
638638
self.string.get_or_insert_with(|| {
639639
let mut s = String::new();
640-
let mut fmt = fmt::Formatter::new(&mut s, fmt::FormattingOptions::new());
640+
let mut fmt = fmt::Formatter::new(&mut s, const { &fmt::FormattingOptions::new() });
641641
let _err = fmt::Display::fmt(&inner, &mut fmt);
642642
s
643643
})

tests/mir-opt/funky_arms.float_to_exponential_common.GVN.panic-abort.diff

+10-2
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,29 @@
2929
debug precision => _8;
3030
let _8: usize;
3131
scope 5 (inlined Formatter::<'_>::precision) {
32+
let mut _23: &std::fmt::FormattingOptions;
3233
}
3334
}
3435
}
3536
}
3637
scope 4 (inlined Formatter::<'_>::sign_plus) {
3738
let mut _20: u32;
3839
let mut _21: u32;
40+
let mut _22: &std::fmt::FormattingOptions;
3941
}
4042

4143
bb0: {
4244
StorageLive(_4);
45+
StorageLive(_22);
4346
StorageLive(_20);
4447
StorageLive(_21);
45-
_21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32);
48+
_22 = copy ((*_1).1: &std::fmt::FormattingOptions);
49+
_21 = copy ((*_22).0: u32);
4650
_20 = BitAnd(move _21, const 1_u32);
4751
StorageDead(_21);
4852
_4 = Ne(move _20, const 0_u32);
4953
StorageDead(_20);
54+
StorageDead(_22);
5055
StorageLive(_5);
5156
switchInt(copy _4) -> [0: bb2, otherwise: bb1];
5257
}
@@ -65,7 +70,10 @@
6570

6671
bb3: {
6772
StorageLive(_6);
68-
_6 = copy (((*_1).0: std::fmt::FormattingOptions).4: std::option::Option<usize>);
73+
StorageLive(_23);
74+
_23 = copy ((*_1).1: &std::fmt::FormattingOptions);
75+
_6 = copy ((*_23).4: std::option::Option<usize>);
76+
StorageDead(_23);
6977
_7 = discriminant(_6);
7078
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb9];
7179
}

tests/mir-opt/funky_arms.float_to_exponential_common.GVN.panic-unwind.diff

+10-2
Original file line numberDiff line numberDiff line change
@@ -29,24 +29,29 @@
2929
debug precision => _8;
3030
let _8: usize;
3131
scope 5 (inlined Formatter::<'_>::precision) {
32+
let mut _23: &std::fmt::FormattingOptions;
3233
}
3334
}
3435
}
3536
}
3637
scope 4 (inlined Formatter::<'_>::sign_plus) {
3738
let mut _20: u32;
3839
let mut _21: u32;
40+
let mut _22: &std::fmt::FormattingOptions;
3941
}
4042

4143
bb0: {
4244
StorageLive(_4);
45+
StorageLive(_22);
4346
StorageLive(_20);
4447
StorageLive(_21);
45-
_21 = copy (((*_1).0: std::fmt::FormattingOptions).0: u32);
48+
_22 = copy ((*_1).1: &std::fmt::FormattingOptions);
49+
_21 = copy ((*_22).0: u32);
4650
_20 = BitAnd(move _21, const 1_u32);
4751
StorageDead(_21);
4852
_4 = Ne(move _20, const 0_u32);
4953
StorageDead(_20);
54+
StorageDead(_22);
5055
StorageLive(_5);
5156
switchInt(copy _4) -> [0: bb2, otherwise: bb1];
5257
}
@@ -65,7 +70,10 @@
6570

6671
bb3: {
6772
StorageLive(_6);
68-
_6 = copy (((*_1).0: std::fmt::FormattingOptions).4: std::option::Option<usize>);
73+
StorageLive(_23);
74+
_23 = copy ((*_1).1: &std::fmt::FormattingOptions);
75+
_6 = copy ((*_23).4: std::option::Option<usize>);
76+
StorageDead(_23);
6977
_7 = discriminant(_6);
7078
switchInt(move _7) -> [1: bb4, 0: bb6, otherwise: bb9];
7179
}

0 commit comments

Comments
 (0)