Skip to content

Commit

Permalink
Provides an alternative print and println macro that don't panic.
Browse files Browse the repository at this point in the history
The `println` and `print` macros provides a simple interface to output
content on the stdout of a program. The macros panic when writing to
stdout, and the failure condition could happen on external conditions.

Take the following rust code:

```rust
fn main() {
  for _ in 0..10000 {
    println!("line") {
  }
}
```

Piping the program output to other utilities could cause the program to
panic, when the pipe is closed.

```bash
produce_logs | head
line
line
line
line
line
line
line
line
line
line
thread '<main>' panicked at 'failed printing to stdout: Broken pipe (os error 32)', ../src/libstd/io/stdio.rs:588
```

Instead of panicking, it would be interesting to allow the developer to
decide what to do with the error result, either ignoring it or panicking
on it's own. This commit implements `try_println` and `try_print` as an
alternative non-panicking macros.

The following code will not panic anymore when the pipe is closed.

```rust
fn main() {
  for _ in 0..10000 {
    if let Err(_) = try_println!("line") {
      std::process::exit(0);
    }
  }
}
```
  • Loading branch information
bltavares committed Oct 30, 2015
1 parent a18e0b2 commit fb6d310
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 5 deletions.
18 changes: 13 additions & 5 deletions src/libstd/io/stdio.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,17 +576,25 @@ pub fn set_print(sink: Box<Write + Send>) -> Option<Box<Write + Send>> {
issue = "0")]
#[doc(hidden)]
pub fn _print(args: fmt::Arguments) {
let result = LOCAL_STDOUT.with(|s| {
let result = _try_print(args);
if let Err(e) = result {
panic!("failed printing to stdout: {}", e);
}
}

#[unstable(feature = "print",
reason = "implementation detail which may disappear or be replaced at any time",
issue = "0")]
#[doc(hidden)]
pub fn _try_print(args: fmt::Arguments) -> io::Result<()> {
LOCAL_STDOUT.with(|s| {
if s.borrow_state() == BorrowState::Unused {
if let Some(w) = s.borrow_mut().as_mut() {
return w.write_fmt(args);
}
}
stdout().write_fmt(args)
});
if let Err(e) = result {
panic!("failed printing to stdout: {}", e);
}
})
}

#[cfg(test)]
Expand Down
52 changes: 52 additions & 0 deletions src/libstd/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,41 @@ macro_rules! print {
($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
}

/// Macro for printing to the standard output.
///
/// Equivalent to the `print!` macro except it does not panic if it fails to
/// write to stdout.
///
/// Note that stdout is frequently line-buffered by default so it may be
/// necessary to use `io::stdout().flush()` to ensure the output is emitted
/// immediately.
///
///
/// # Examples
///
/// ```
/// use std::io::{self, Write};
///
/// try_print!("this ").unwrap();
/// try_print!("will ").unwrap();
/// try_print!("be ").unwrap();
/// try_print!("on ").unwrap();
/// try_print!("the ").unwrap();
/// try_print!("same ").unwrap();
/// try_print!("line ").unwrap();
///
/// io::stdout().flush().unwrap();
///
/// try_print!("this string has a newline, why not choose println! instead?\n").unwrap();
///
/// io::stdout().flush().unwrap();
/// ```
#[macro_export]
#[allow_internal_unstable]
macro_rules! try_print {
($($arg:tt)*) => ($crate::io::_try_print(format_args!($($arg)*)));
}

/// Macro for printing to the standard output, with a newline.
///
/// Use the `format!` syntax to write data to the standard output.
Expand All @@ -120,6 +155,23 @@ macro_rules! println {
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
}

/// Macro for printing to the standard output, with a newline.
///
/// Use the `format!` syntax to write data to the standard output.
/// See `std::fmt` for more information.
///
/// # Examples
///
/// ```
/// try_println!("hello there!").unwrap();
/// try_println!("format {} arguments", "some").unwrap();
/// ```
#[macro_export]
macro_rules! try_println {
($fmt:expr) => (try_print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (try_print!(concat!($fmt, "\n"), $($arg)*));
}

/// Helper macro for unwrapping `Result` values while returning early with an
/// error if the value of the expression is `Err`. Can only be used in
/// functions that return `Result` because of the early return of `Err` that
Expand Down

0 comments on commit fb6d310

Please sign in to comment.