Skip to content

Commit

Permalink
std: Stabilize std::fmt
Browse files Browse the repository at this point in the history
This commit applies the stabilization of std::fmt as outlined in [RFC 380][rfc].
There are a number of breaking changes as a part of this commit which will need
to be handled to migrated old code:

* A number of formatting traits have been removed: String, Bool, Char, Unsigned,
  Signed, and Float. It is recommended to instead use Show wherever possible or
  to use adaptor structs to implement other methods of formatting.

* The format specifier for Boolean has changed from `t` to `b`.

* The enum `FormatError` has been renamed to `Error` as well as becoming a unit
  struct instead of an enum. The `WriteError` variant no longer exists.

* The `format_args_method!` macro has been removed with no replacement. Alter
  code to use the `format_args!` macro instead.

* The public fields of a `Formatter` have become read-only with no replacement.
  Use a new formatting string to alter the formatting flags in combination with
  the `write!` macro. The fields can be accessed through accessor methods on the
  `Formatter` structure.

Other than these breaking changes, the contents of std::fmt should now also all
contain stability markers. Most of them are still #[unstable] or #[experimental]

[rfc]: https://github.com/rust-lang/rfcs/blob/master/text/0380-stabilize-std-fmt.md
[breaking-change]

Closes rust-lang#18904
  • Loading branch information
alexcrichton committed Nov 19, 2014
1 parent e09d986 commit 4af3494
Show file tree
Hide file tree
Showing 48 changed files with 291 additions and 347 deletions.
149 changes: 67 additions & 82 deletions src/libcore/fmt/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@
#![allow(unused_variables)]

pub use self::FormatError::*;

use any;
use cell::{Cell, Ref, RefMut};
use iter::{Iterator, range};
Expand All @@ -23,10 +21,9 @@ use option::{Option, Some, None};
use ops::Deref;
use result::{Ok, Err};
use result;
use slice::{AsSlice, SlicePrelude};
use slice::SlicePrelude;
use slice;
use str::StrPrelude;
use str;

pub use self::num::radix;
pub use self::num::Radix;
Expand All @@ -36,18 +33,16 @@ mod num;
mod float;
pub mod rt;

pub type Result = result::Result<(), FormatError>;
#[experimental = "core and I/O reconciliation may alter this definition"]
pub type Result = result::Result<(), Error>;

/// The error type which is returned from formatting a message into a stream.
///
/// This type does not support transmission of an error other than that an error
/// occurred. Any extra information must be arranged to be transmitted through
/// some other means.
pub enum FormatError {
/// A generic write error occurred during formatting, no other information
/// is transmitted via this variant.
WriteError,
}
#[experimental = "core and I/O reconciliation may alter this definition"]
pub struct Error;

/// A collection of methods that are required to format a message into a stream.
///
Expand All @@ -58,6 +53,7 @@ pub enum FormatError {
/// This trait should generally not be implemented by consumers of the standard
/// library. The `write!` macro accepts an instance of `io::Writer`, and the
/// `io::Writer` trait is favored over implementing this trait.
#[experimental = "waiting for core and I/O reconciliation"]
pub trait FormatWriter {
/// Writes a slice of bytes into this writer, returning whether the write
/// succeeded.
Expand All @@ -81,17 +77,13 @@ pub trait FormatWriter {
/// A struct to represent both where to emit formatting strings to and how they
/// should be formatted. A mutable version of this is passed to all formatting
/// traits.
#[unstable = "name may change and implemented traits are also unstable"]
pub struct Formatter<'a> {
/// Flags for formatting (packed version of rt::Flag)
pub flags: uint,
/// Character used as 'fill' whenever there is alignment
pub fill: char,
/// Boolean indication of whether the output should be left-aligned
pub align: rt::Alignment,
/// Optionally specified integer width that the output should be
pub width: Option<uint>,
/// Optionally specified precision for numeric types
pub precision: Option<uint>,
flags: uint,
fill: char,
align: rt::Alignment,
width: Option<uint>,
precision: Option<uint>,

buf: &'a mut FormatWriter+'a,
curarg: slice::Items<'a, Argument<'a>>,
Expand All @@ -104,6 +96,7 @@ enum Void {}
/// family of functions. It contains a function to format the given value. At
/// compile time it is ensured that the function and the value have the correct
/// types, and then this struct is used to canonicalize arguments to one type.
#[experimental = "implementation detail of the `format_args!` macro"]
pub struct Argument<'a> {
formatter: extern "Rust" fn(&Void, &mut Formatter) -> Result,
value: &'a Void,
Expand All @@ -115,6 +108,7 @@ impl<'a> Arguments<'a> {
/// which is valid because the compiler performs all necessary validation to
/// ensure that the resulting call to format/write would be safe.
#[doc(hidden)] #[inline]
#[experimental = "implementation detail of the `format_args!` macro"]
pub unsafe fn new<'a>(pieces: &'static [&'static str],
args: &'a [Argument<'a>]) -> Arguments<'a> {
Arguments {
Expand All @@ -128,6 +122,7 @@ impl<'a> Arguments<'a> {
/// The `pieces` array must be at least as long as `fmt` to construct
/// a valid Arguments structure.
#[doc(hidden)] #[inline]
#[experimental = "implementation detail of the `format_args!` macro"]
pub unsafe fn with_placeholders<'a>(pieces: &'static [&'static str],
fmt: &'static [rt::Argument<'static>],
args: &'a [Argument<'a>]) -> Arguments<'a> {
Expand All @@ -148,6 +143,7 @@ impl<'a> Arguments<'a> {
/// and pass it to a function or closure, passed as the first argument. The
/// macro validates the format string at compile-time so usage of the `write`
/// and `format` functions can be safely performed.
#[stable]
pub struct Arguments<'a> {
// Format string pieces to print.
pieces: &'a [&'a str],
Expand All @@ -169,84 +165,57 @@ impl<'a> Show for Arguments<'a> {
/// When a format is not otherwise specified, types are formatted by ascribing
/// to this trait. There is not an explicit way of selecting this trait to be
/// used for formatting, it is only if no other format is specified.
#[unstable = "I/O and core have yet to be reconciled"]
pub trait Show for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `b` character
pub trait Bool for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `c` character
pub trait Char for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `i` and `d` characters
pub trait Signed for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `u` character
pub trait Unsigned for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `o` character
#[unstable = "I/O and core have yet to be reconciled"]
pub trait Octal for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `t` character
#[unstable = "I/O and core have yet to be reconciled"]
pub trait Binary for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `x` character
#[unstable = "I/O and core have yet to be reconciled"]
pub trait LowerHex for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `X` character
#[unstable = "I/O and core have yet to be reconciled"]
pub trait UpperHex for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `s` character
pub trait String for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `p` character
#[unstable = "I/O and core have yet to be reconciled"]
pub trait Pointer for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `f` character
pub trait Float for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `e` character
#[unstable = "I/O and core have yet to be reconciled"]
pub trait LowerExp for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
}

/// Format trait for the `E` character
#[unstable = "I/O and core have yet to be reconciled"]
pub trait UpperExp for Sized? {
/// Formats the value using the given formatter.
fn fmt(&self, &mut Formatter) -> Result;
Expand All @@ -271,6 +240,8 @@ static DEFAULT_ARGUMENT: rt::Argument<'static> = rt::Argument {
///
/// * output - the buffer to write output to
/// * args - the precompiled arguments generated by `format_args!`
#[experimental = "libcore and I/O have yet to be reconciled, and this is an \
implementation detail which should not otherwise be exported"]
pub fn write(output: &mut FormatWriter, args: &Arguments) -> Result {
let mut formatter = Formatter {
flags: 0,
Expand Down Expand Up @@ -368,6 +339,7 @@ impl<'a> Formatter<'a> {
///
/// This function will correctly account for the flags provided as well as
/// the minimum width. It will not take precision into account.
#[unstable = "definition may change slightly over time"]
pub fn pad_integral(&mut self,
is_positive: bool,
prefix: &str,
Expand Down Expand Up @@ -440,6 +412,7 @@ impl<'a> Formatter<'a> {
/// is longer than this length
///
/// Notably this function ignored the `flag` parameters
#[unstable = "definition may change slightly over time"]
pub fn pad(&mut self, s: &str) -> Result {
// Make sure there's a fast path up front
if self.width.is_none() && self.precision.is_none() {
Expand Down Expand Up @@ -516,19 +489,48 @@ impl<'a> Formatter<'a> {

/// Writes some data to the underlying buffer contained within this
/// formatter.
#[unstable = "reconciling core and I/O may alter this definition"]
pub fn write(&mut self, data: &[u8]) -> Result {
self.buf.write(data)
}

/// Writes some formatted information into this instance
#[unstable = "reconciling core and I/O may alter this definition"]
pub fn write_fmt(&mut self, fmt: &Arguments) -> Result {
write(self.buf, fmt)
}

/// Flags for formatting (packed version of rt::Flag)
#[experimental = "return type may change and method was just created"]
pub fn flags(&self) -> uint { self.flags }

/// Character used as 'fill' whenever there is alignment
#[unstable = "method was just created"]
pub fn fill(&self) -> char { self.fill }

/// Flag indicating what form of alignment was requested
#[unstable = "method was just created"]
pub fn align(&self) -> rt::Alignment { self.align }

/// Optionally specified integer width that the output should be
#[unstable = "method was just created"]
pub fn width(&self) -> Option<uint> { self.width }

/// Optionally specified precision for numeric types
#[unstable = "method was just created"]
pub fn precision(&self) -> Option<uint> { self.precision }
}

impl Show for Error {
fn fmt(&self, f: &mut Formatter) -> Result {
"an error occurred when formatting an argument".fmt(f)
}
}

/// This is a function which calls are emitted to by the compiler itself to
/// create the Argument structures that are passed into the `format` function.
#[doc(hidden)] #[inline]
#[experimental = "implementation detail of the `format_args!` macro"]
pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter) -> Result,
t: &'a T) -> Argument<'a> {
unsafe {
Expand All @@ -542,15 +544,17 @@ pub fn argument<'a, T>(f: extern "Rust" fn(&T, &mut Formatter) -> Result,
/// When the compiler determines that the type of an argument *must* be a string
/// (such as for select), then it invokes this method.
#[doc(hidden)] #[inline]
#[experimental = "implementation detail of the `format_args!` macro"]
pub fn argumentstr<'a>(s: &'a &str) -> Argument<'a> {
argument(String::fmt, s)
argument(Show::fmt, s)
}

/// When the compiler determines that the type of an argument *must* be a uint
/// (such as for plural), then it invokes this method.
#[doc(hidden)] #[inline]
#[experimental = "implementation detail of the `format_args!` macro"]
pub fn argumentuint<'a>(s: &'a uint) -> Argument<'a> {
argument(Unsigned::fmt, s)
argument(Show::fmt, s)
}

// Implementations of the core formatting traits
Expand All @@ -565,32 +569,26 @@ impl<'a> Show for &'a Show+'a {
fn fmt(&self, f: &mut Formatter) -> Result { (*self).fmt(f) }
}

impl Bool for bool {
fn fmt(&self, f: &mut Formatter) -> Result {
String::fmt(if *self { "true" } else { "false" }, f)
}
}

impl<T: str::Str> String for T {
impl Show for bool {
fn fmt(&self, f: &mut Formatter) -> Result {
f.pad(self.as_slice())
Show::fmt(if *self { "true" } else { "false" }, f)
}
}

impl String for str {
impl Show for str {
fn fmt(&self, f: &mut Formatter) -> Result {
f.pad(self)
}
}

impl Char for char {
impl Show for char {
fn fmt(&self, f: &mut Formatter) -> Result {
use char::Char;

let mut utf8 = [0u8, ..4];
let amt = self.encode_utf8(&mut utf8).unwrap_or(0);
let s: &str = unsafe { mem::transmute(utf8[..amt]) };
String::fmt(s, f)
Show::fmt(s, f)
}
}

Expand Down Expand Up @@ -620,7 +618,7 @@ impl<'a, T> Pointer for &'a mut T {
}

macro_rules! floating(($ty:ident) => {
impl Float for $ty {
impl Show for $ty {
fn fmt(&self, fmt: &mut Formatter) -> Result {
use num::Float;

Expand Down Expand Up @@ -688,19 +686,6 @@ floating!(f64)

// Implementation of Show for various core types

macro_rules! delegate(($ty:ty to $other:ident) => {
impl Show for $ty {
fn fmt(&self, f: &mut Formatter) -> Result {
$other::fmt(self, f)
}
}
})
delegate!(str to String)
delegate!(bool to Bool)
delegate!(char to Char)
delegate!(f32 to Float)
delegate!(f64 to Float)

impl<T> Show for *const T {
fn fmt(&self, f: &mut Formatter) -> Result { Pointer::fmt(self, f) }
}
Expand Down
Loading

6 comments on commit 4af3494

@alexcrichton
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

r=aturon

@thestinger
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(alexcrichton editing away r- due to rust-lang#19040 (comment))

It's premature to mark print! and println! as stable without addressing the concerns with them or at least deciding that the design is acceptable and documenting the compromises. I would expect the fact that Rust has a wrapped stdout requiring virtual method calls to fall under runtime reform, and I also think the misuse of panic! to deal with unpredictable runtime errors needs to be addressed in some way.

@nikomatsakis
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thestinger A couple of points:

  1. This stabilization change went through the RFC process, so I would say that it has been documented that the design is considered acceptable. In general, it is much better to make comments during the RFC process than to wait until the PR.
  2. Your objections regarding runtime reform seem focused on implementation details, but this PR is focused entirely on the interface to println!. println! is a convenience macro; whatever changes occcur as part of runtime reform, it seems clear we'll be able to go on supporting some convenient means of printing to stdio/stderr.
  3. Regarding panic, I don't see a major issue here. If you don't want to panic on I/O failure, don't use the println! macro. If all you want is a convenient way to print something out, go for it.

@thestinger
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This stabilization change went through the RFC process, so I would say that it has been documented that the design is considered acceptable. In general, it is much better to make comments during the RFC process than to wait until the PR.

I don't have time to read through all of the RFCs. Only a fraction of those proposals make it to real pull requests, and many significant changes skip the process altogether anyway.

Your objections regarding runtime reform seem focused on implementation details, but this PR is focused entirely on the interface to println!. println! is a convenience macro; whatever changes occcur as part of runtime reform, it seems clear we'll be able to go on supporting some convenient means of printing to stdio/stderr.

It returning a value or having different buffering semantics would be a breaking change to the API.

Regarding panic, I don't see a major issue here. If you don't want to panic on I/O failure, don't use the println! macro. If all you want is a convenient way to print something out, go for it.

It doesn't just panic on I/O failure, it panics on broken pipes which is a completely normal part of *nix programming. Nearly every program used in shell pipelines has to cope with broken pipes when a program like head closes early.

@thestinger
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If println! isn't intended to be usable in small command-line programs, then I don't know why it exists.

@nikomatsakis
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.