Skip to content

Commit fb6d310

Browse files
committed
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 '<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); } } } ```
1 parent a18e0b2 commit fb6d310

File tree

2 files changed

+65
-5
lines changed

2 files changed

+65
-5
lines changed

src/libstd/io/stdio.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -576,17 +576,25 @@ pub fn set_print(sink: Box<Write + Send>) -> Option<Box<Write + Send>> {
576576
issue = "0")]
577577
#[doc(hidden)]
578578
pub fn _print(args: fmt::Arguments) {
579-
let result = LOCAL_STDOUT.with(|s| {
579+
let result = _try_print(args);
580+
if let Err(e) = result {
581+
panic!("failed printing to stdout: {}", e);
582+
}
583+
}
584+
585+
#[unstable(feature = "print",
586+
reason = "implementation detail which may disappear or be replaced at any time",
587+
issue = "0")]
588+
#[doc(hidden)]
589+
pub fn _try_print(args: fmt::Arguments) -> io::Result<()> {
590+
LOCAL_STDOUT.with(|s| {
580591
if s.borrow_state() == BorrowState::Unused {
581592
if let Some(w) = s.borrow_mut().as_mut() {
582593
return w.write_fmt(args);
583594
}
584595
}
585596
stdout().write_fmt(args)
586-
});
587-
if let Err(e) = result {
588-
panic!("failed printing to stdout: {}", e);
589-
}
597+
})
590598
}
591599

592600
#[cfg(test)]

src/libstd/macros.rs

+52
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,41 @@ macro_rules! print {
9898
($($arg:tt)*) => ($crate::io::_print(format_args!($($arg)*)));
9999
}
100100

101+
/// Macro for printing to the standard output.
102+
///
103+
/// Equivalent to the `print!` macro except it does not panic if it fails to
104+
/// write to stdout.
105+
///
106+
/// Note that stdout is frequently line-buffered by default so it may be
107+
/// necessary to use `io::stdout().flush()` to ensure the output is emitted
108+
/// immediately.
109+
///
110+
///
111+
/// # Examples
112+
///
113+
/// ```
114+
/// use std::io::{self, Write};
115+
///
116+
/// try_print!("this ").unwrap();
117+
/// try_print!("will ").unwrap();
118+
/// try_print!("be ").unwrap();
119+
/// try_print!("on ").unwrap();
120+
/// try_print!("the ").unwrap();
121+
/// try_print!("same ").unwrap();
122+
/// try_print!("line ").unwrap();
123+
///
124+
/// io::stdout().flush().unwrap();
125+
///
126+
/// try_print!("this string has a newline, why not choose println! instead?\n").unwrap();
127+
///
128+
/// io::stdout().flush().unwrap();
129+
/// ```
130+
#[macro_export]
131+
#[allow_internal_unstable]
132+
macro_rules! try_print {
133+
($($arg:tt)*) => ($crate::io::_try_print(format_args!($($arg)*)));
134+
}
135+
101136
/// Macro for printing to the standard output, with a newline.
102137
///
103138
/// Use the `format!` syntax to write data to the standard output.
@@ -120,6 +155,23 @@ macro_rules! println {
120155
($fmt:expr, $($arg:tt)*) => (print!(concat!($fmt, "\n"), $($arg)*));
121156
}
122157

158+
/// Macro for printing to the standard output, with a newline.
159+
///
160+
/// Use the `format!` syntax to write data to the standard output.
161+
/// See `std::fmt` for more information.
162+
///
163+
/// # Examples
164+
///
165+
/// ```
166+
/// try_println!("hello there!").unwrap();
167+
/// try_println!("format {} arguments", "some").unwrap();
168+
/// ```
169+
#[macro_export]
170+
macro_rules! try_println {
171+
($fmt:expr) => (try_print!(concat!($fmt, "\n")));
172+
($fmt:expr, $($arg:tt)*) => (try_print!(concat!($fmt, "\n"), $($arg)*));
173+
}
174+
123175
/// Helper macro for unwrapping `Result` values while returning early with an
124176
/// error if the value of the expression is `Err`. Can only be used in
125177
/// functions that return `Result` because of the early return of `Err` that

0 commit comments

Comments
 (0)