Skip to content

Commit 9c84c6b

Browse files
authored
Rollup merge of rust-lang#74056 - fusion-engineering-forks:fmt-arguments-as-str, r=Amanieu
Add Arguments::as_str(). There exist quite a few macros in the Rust ecosystem which use `format_args!()` for formatting, but special case the one-argument case for optimization: ```rust #[macro_export] macro_rules! some_macro { ($s:expr) => { /* print &str directly, no formatting, no buffers */ }; ($s:expr, $($tt:tt)*) => { /* use format_args to write to a buffer first */ } } ``` E.g. [here](https://github.com/rust-embedded/cortex-m-semihosting/blob/7a961f0fbe6eb1b29a7ebde4bad4b9cf5f842b31/src/macros.rs#L48-L58), [here](https://github.com/rust-lang-nursery/failure/blob/20f9a9e223b7cd71aed541d050cc73a747fc00c4/src/macros.rs#L9-L17), and [here](https://github.com/fusion-engineering/px4-rust/blob/7b679cd6da9ffd95f36f6526d88345f8b36121da/px4/src/logging.rs#L45-L52). The problem with these is that a forgotten argument such as in `some_macro!("{}")` will not be diagnosed, but just prints `"{}"`. With this PR, it is possible to handle the no-arguments case separately *after* `format_args!()`, while simplifying the macro. Then these macros can give the proper error about a missing argument, just like `print!("{}")` does, while still using the same optimized implementation as before. This is even more important with [RFC 2795](rust-lang/rfcs#2795), to make sure `some_macro!("{some_variable}")` works as expected.
2 parents be3b972 + 9c3353b commit 9c84c6b

File tree

6 files changed

+322
-290
lines changed

6 files changed

+322
-290
lines changed

src/libcore/fmt/mod.rs

+44-3
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ impl<'a> Arguments<'a> {
324324
#[doc(hidden)]
325325
#[inline]
326326
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
327-
pub fn new_v1(pieces: &'a [&'a str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> {
327+
pub fn new_v1(pieces: &'a [&'static str], args: &'a [ArgumentV1<'a>]) -> Arguments<'a> {
328328
Arguments { pieces, fmt: None, args }
329329
}
330330

@@ -338,7 +338,7 @@ impl<'a> Arguments<'a> {
338338
#[inline]
339339
#[unstable(feature = "fmt_internals", reason = "internal to format_args!", issue = "none")]
340340
pub fn new_v1_formatted(
341-
pieces: &'a [&'a str],
341+
pieces: &'a [&'static str],
342342
args: &'a [ArgumentV1<'a>],
343343
fmt: &'a [rt::v1::Argument],
344344
) -> Arguments<'a> {
@@ -399,7 +399,7 @@ impl<'a> Arguments<'a> {
399399
#[derive(Copy, Clone)]
400400
pub struct Arguments<'a> {
401401
// Format string pieces to print.
402-
pieces: &'a [&'a str],
402+
pieces: &'a [&'static str],
403403

404404
// Placeholder specs, or `None` if all specs are default (as in "{}{}").
405405
fmt: Option<&'a [rt::v1::Argument]>,
@@ -409,6 +409,47 @@ pub struct Arguments<'a> {
409409
args: &'a [ArgumentV1<'a>],
410410
}
411411

412+
impl<'a> Arguments<'a> {
413+
/// Get the formatted string, if it has no arguments to be formatted.
414+
///
415+
/// This can be used to avoid allocations in the most trivial case.
416+
///
417+
/// # Examples
418+
///
419+
/// ```rust
420+
/// #![feature(fmt_as_str)]
421+
///
422+
/// use core::fmt::Arguments;
423+
///
424+
/// fn write_str(_: &str) { /* ... */ }
425+
///
426+
/// fn write_fmt(args: &Arguments) {
427+
/// if let Some(s) = args.as_str() {
428+
/// write_str(s)
429+
/// } else {
430+
/// write_str(&args.to_string());
431+
/// }
432+
/// }
433+
/// ```
434+
///
435+
/// ```rust
436+
/// #![feature(fmt_as_str)]
437+
///
438+
/// assert_eq!(format_args!("hello").as_str(), Some("hello"));
439+
/// assert_eq!(format_args!("").as_str(), Some(""));
440+
/// assert_eq!(format_args!("{}", 1).as_str(), None);
441+
/// ```
442+
#[unstable(feature = "fmt_as_str", issue = "74442")]
443+
#[inline]
444+
pub fn as_str(&self) -> Option<&'static str> {
445+
match (self.pieces, self.args) {
446+
([], []) => Some(""),
447+
([s], []) => Some(s),
448+
_ => None,
449+
}
450+
}
451+
}
452+
412453
#[stable(feature = "rust1", since = "1.0.0")]
413454
impl Debug for Arguments<'_> {
414455
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {

src/libcore/macros/mod.rs

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,12 @@ macro_rules! panic {
66
() => (
77
$crate::panic!("explicit panic")
88
);
9-
($msg:expr) => (
9+
($msg:literal) => (
1010
$crate::panicking::panic($msg)
1111
);
12+
($msg:expr) => (
13+
$crate::panic!("{}", $crate::convert::identity::<&str>($msg))
14+
);
1215
($msg:expr,) => (
1316
$crate::panic!($msg)
1417
);

src/libcore/panicking.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ use crate::panic::{Location, PanicInfo};
3636
#[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))]
3737
#[track_caller]
3838
#[lang = "panic"] // needed by codegen for panic on overflow and other `Assert` MIR terminators
39-
pub fn panic(expr: &str) -> ! {
39+
pub fn panic(expr: &'static str) -> ! {
4040
if cfg!(feature = "panic_immediate_abort") {
4141
super::intrinsics::abort()
4242
}

0 commit comments

Comments
 (0)