From fb6d310c65f7b077c919e28c82118af559a06399 Mon Sep 17 00:00:00 2001 From: Bruno Tavares Date: Thu, 29 Oct 2015 21:02:13 -0200 Subject: [PATCH] Provides an alternative print and println macro that don't panic. 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 '
' 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); } } } ``` --- src/libstd/io/stdio.rs | 18 +++++++++++---- src/libstd/macros.rs | 52 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/src/libstd/io/stdio.rs b/src/libstd/io/stdio.rs index 31b881bebf05f..56186a933510c 100644 --- a/src/libstd/io/stdio.rs +++ b/src/libstd/io/stdio.rs @@ -576,17 +576,25 @@ pub fn set_print(sink: Box) -> Option> { 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)] diff --git a/src/libstd/macros.rs b/src/libstd/macros.rs index a88ddb997f61d..8b9ac78f8a697 100644 --- a/src/libstd/macros.rs +++ b/src/libstd/macros.rs @@ -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. @@ -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