Skip to content

Commit 9a5f95a

Browse files
committed
Implement a format_args!() macro
The purpose of this macro is to further reduce the number of allocations which occur when dealing with formatting strings. This macro will perform all of the static analysis necessary to validate that a format string is safe, and then it will wrap up the "format string" into an opaque struct which can then be passed around. Two safe functions are added (write/format) which take this opaque argument structure, unwrap it, and then call the unsafe version of write/format (in an unsafe block). Other than these two functions, it is not intended for anyone to ever look inside this opaque struct. The macro looks a bit odd, but mostly because of rvalue lifetimes this is the only way for it to be safe that I know of. Example use-cases of this are: * third-party libraries can use the default formatting syntax without any forced allocations * the fail!() macro can avoid allocating the format string * the logging macros can avoid allocation any strings
1 parent 6216661 commit 9a5f95a

File tree

6 files changed

+234
-82
lines changed

6 files changed

+234
-82
lines changed

Diff for: src/libstd/fmt/mod.rs

+75-16
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ Some examples of the `format!` extension are:
3636
format!("Hello") // => ~"Hello"
3737
format!("Hello, {:s}!", "world") // => ~"Hello, world!"
3838
format!("The number is {:d}", 1) // => ~"The number is 1"
39-
format!("{}", ~[3, 4]) // => ~"~[3, 4]"
39+
format!("{:?}", ~[3, 4]) // => ~"~[3, 4]"
4040
format!("{value}", value=4) // => ~"4"
4141
format!("{} {}", 1, 2) // => ~"1 2"
4242
~~~
@@ -363,6 +363,32 @@ pub struct Argument<'self> {
363363
priv value: &'self util::Void,
364364
}
365365

366+
impl<'self> Arguments<'self> {
367+
/// When using the format_args!() macro, this function is used to generate the
368+
/// Arguments structure. The compiler inserts an `unsafe` block to call this,
369+
/// which is valid because the compiler performs all necessary validation to
370+
/// ensure that the resulting call to format/write would be safe.
371+
#[doc(hidden)] #[inline]
372+
pub unsafe fn new<'a>(fmt: &'static [rt::Piece<'static>],
373+
args: &'a [Argument<'a>]) -> Arguments<'a> {
374+
Arguments{ fmt: cast::transmute(fmt), args: args }
375+
}
376+
}
377+
378+
/// This structure represents a safely precompiled version of a format string
379+
/// and its arguments. This cannot be generated at runtime because it cannot
380+
/// safely be done so, so no constructors are given and the fields are private
381+
/// to prevent modification.
382+
///
383+
/// The `format_args!` macro will safely create an instance of this structure
384+
/// and pass it to a user-supplied function. The macro validates the format
385+
/// string at compile-time so usage of the `write` and `format` functions can
386+
/// be safely performed.
387+
pub struct Arguments<'self> {
388+
priv fmt: &'self [rt::Piece<'self>],
389+
priv args: &'self [Argument<'self>],
390+
}
391+
366392
/// When a format is not otherwise specified, types are formatted by ascribing
367393
/// to this trait. There is not an explicit way of selecting this trait to be
368394
/// used for formatting, it is only if no other format is specified.
@@ -410,6 +436,26 @@ pub trait Float { fn fmt(&Self, &mut Formatter); }
410436
/// and a list of arguments. The arguments will be formatted according to the
411437
/// specified format string into the output stream provided.
412438
///
439+
/// # Arguments
440+
///
441+
/// * output - the buffer to write output to
442+
/// * args - the precompiled arguments generated by `format_args!`
443+
///
444+
/// # Example
445+
///
446+
/// ~~~{.rust}
447+
/// use std::fmt;
448+
/// let w: &mut io::Writer = ...;
449+
/// format_args!(|args| { fmt::write(w, args) }, "Hello, {}!", "world");
450+
/// ~~~
451+
pub fn write(output: &mut io::Writer, args: &Arguments) {
452+
unsafe { write_unsafe(output, args.fmt, args.args) }
453+
}
454+
455+
/// The `write_unsafe` function takes an output stream, a precompiled format
456+
/// string, and a list of arguments. The arguments will be formatted according
457+
/// to the specified format string into the output stream provided.
458+
///
413459
/// See the documentation for `format` for why this function is unsafe and care
414460
/// should be taken if calling it manually.
415461
///
@@ -426,8 +472,9 @@ pub trait Float { fn fmt(&Self, &mut Formatter); }
426472
///
427473
/// Note that this function assumes that there are enough arguments for the
428474
/// format string.
429-
pub unsafe fn write(output: &mut io::Writer,
430-
fmt: &[rt::Piece], args: &[Argument]) {
475+
pub unsafe fn write_unsafe(output: &mut io::Writer,
476+
fmt: &[rt::Piece],
477+
args: &[Argument]) {
431478
let mut formatter = Formatter {
432479
flags: 0,
433480
width: None,
@@ -446,6 +493,25 @@ pub unsafe fn write(output: &mut io::Writer,
446493
/// The format function takes a precompiled format string and a list of
447494
/// arguments, to return the resulting formatted string.
448495
///
496+
/// # Arguments
497+
///
498+
/// * args - a structure of arguments generated via the `format_args!` macro.
499+
/// Because this structure can only be safely generated at
500+
/// compile-time, this function is safe.
501+
///
502+
/// # Example
503+
///
504+
/// ~~~{.rust}
505+
/// use std::fmt;
506+
/// let s = format_args!(fmt::format, "Hello, {}!", "world");
507+
/// assert_eq!(s, "Hello, world!");
508+
/// ~~~
509+
pub fn format(args: &Arguments) -> ~str {
510+
unsafe { format_unsafe(args.fmt, args.args) }
511+
}
512+
513+
/// The unsafe version of the formatting function.
514+
///
449515
/// This is currently an unsafe function because the types of all arguments
450516
/// aren't verified by immediate callers of this function. This currently does
451517
/// not validate that the correct types of arguments are specified for each
@@ -465,9 +531,9 @@ pub unsafe fn write(output: &mut io::Writer,
465531
///
466532
/// Note that this function assumes that there are enough arguments for the
467533
/// format string.
468-
pub unsafe fn format(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
534+
pub unsafe fn format_unsafe(fmt: &[rt::Piece], args: &[Argument]) -> ~str {
469535
let mut output = MemWriter::new();
470-
write(&mut output as &mut io::Writer, fmt, args);
536+
write_unsafe(&mut output as &mut io::Writer, fmt, args);
471537
return str::from_utf8_owned(output.inner());
472538
}
473539

@@ -740,7 +806,7 @@ impl<'self> Formatter<'self> {
740806

741807
/// This is a function which calls are emitted to by the compiler itself to
742808
/// create the Argument structures that are passed into the `format` function.
743-
#[doc(hidden)]
809+
#[doc(hidden)] #[inline]
744810
pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter),
745811
t: &'a T) -> Argument<'a> {
746812
unsafe {
@@ -753,14 +819,14 @@ pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter),
753819

754820
/// When the compiler determines that the type of an argument *must* be a string
755821
/// (such as for select), then it invokes this method.
756-
#[doc(hidden)]
822+
#[doc(hidden)] #[inline]
757823
pub fn argumentstr<'a>(s: &'a &str) -> Argument<'a> {
758824
argument(String::fmt, s)
759825
}
760826

761827
/// When the compiler determines that the type of an argument *must* be a uint
762828
/// (such as for plural), then it invokes this method.
763-
#[doc(hidden)]
829+
#[doc(hidden)] #[inline]
764830
pub fn argumentuint<'a>(s: &'a uint) -> Argument<'a> {
765831
argument(Unsigned::fmt, s)
766832
}
@@ -899,14 +965,8 @@ impl<T> Pointer for *T {
899965
}
900966
}
901967
}
902-
903968
impl<T> Pointer for *mut T {
904-
fn fmt(t: &*mut T, f: &mut Formatter) {
905-
f.flags |= 1 << (parse::FlagAlternate as uint);
906-
do ::uint::to_str_bytes(*t as uint, 16) |buf| {
907-
f.pad_integral(buf, "0x", true);
908-
}
909-
}
969+
fn fmt(t: &*mut T, f: &mut Formatter) { Pointer::fmt(&(*t as *T), f) }
910970
}
911971

912972
// Implementation of Default for various core types
@@ -940,7 +1000,6 @@ delegate!(f64 to Float)
9401000
impl<T> Default for *T {
9411001
fn fmt(me: &*T, f: &mut Formatter) { Pointer::fmt(me, f) }
9421002
}
943-
9441003
impl<T> Default for *mut T {
9451004
fn fmt(me: &*mut T, f: &mut Formatter) { Pointer::fmt(me, f) }
9461005
}

Diff for: src/libsyntax/ext/base.rs

+2
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,8 @@ pub fn syntax_expander_table() -> SyntaxEnv {
161161
builtin_normal_tt_no_ctxt(ext::ifmt::expand_write));
162162
syntax_expanders.insert(intern(&"writeln"),
163163
builtin_normal_tt_no_ctxt(ext::ifmt::expand_writeln));
164+
syntax_expanders.insert(intern(&"format_args"),
165+
builtin_normal_tt_no_ctxt(ext::ifmt::expand_format_args));
164166
syntax_expanders.insert(
165167
intern(&"auto_encode"),
166168
@SE(ItemDecorator(ext::auto_encode::expand_auto_encode)));

0 commit comments

Comments
 (0)