From f62c6d7ff1887055520782af619cb78b562ae8ea Mon Sep 17 00:00:00 2001 From: Zack Weinberg Date: Mon, 23 Jan 2017 14:30:17 -0500 Subject: [PATCH 1/2] RFC: add `eprint(ln)!` --- text/0000-eprintln.md | 129 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 text/0000-eprintln.md diff --git a/text/0000-eprintln.md b/text/0000-eprintln.md new file mode 100644 index 00000000000..d10aa8d9aa0 --- /dev/null +++ b/text/0000-eprintln.md @@ -0,0 +1,129 @@ +- Feature Name: eprintln +- Start Date: 2017-01-23 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +This RFC proposes the addition of two macros to the global prelude, +`eprint!` and `eprintln!`. These are exactly the same as `print!` and +`println!`, respectively, except that they write to standard error +instead of standard output. + +# Motivation +[motivation]: #motivation + +This proposal will improve the ergonomics of the Rust language for +development of command-line tools and "back end" / "computational +kernel" programs. Such programs need to maintain a distinction +between their _primary output_, which will be fed to the next element +in a computational "pipeline", and their _status reports_, which +should go directly to the user. Conventionally, standard output +should receive the primary output and standard error should receive +status reports. + +At present, writing text to standard output is very easy, using the +`print(ln)!` macros, but writing text to standard error is +significantly more work: compare + + println!("out of cheese error: {}", 42); + writeln!(stderr(), "out of cheese error: {}", 42).unwrap(); + +The latter may also require the addition of `use std::io::stderr` +and/or `use std::io::Write;` to the top of the file. + +Because writing to stderr is more work, and requires introduction of +more concepts, all of the tutorial documentation for the language uses +`println!` for error messages, which teaches bad habits. + +# Detailed design +[design]: #detailed-design + +Most of the design is already nailed down by the existing `println!` +macro. It is my intention to clone the existing definition verbatim, +changing only the name and the I/O stream written to. Anything other +than strict parallelism with `println!` would be surprising and +confusing. + +There remain two design decisions to make: + + * Should there be an `eprint!` analogous to `print!`? (That is, a + macro that writes formatted text to stderr _without_ appending a + newline.) I suspect that it will be rarely used, but I also + suspect that its absence will be surprising. Leaving out `eprint!` + might enable a better choice of name for `eprintln!` (see below). + + * What should the name(s) of the macro(s) be? There were four + candidates proposed in [the pre-RFC][pre-rfc]: + + * `eprintln!` and `eprint!` -- easy to type, and two different + people said they were already using these names in their own + code; but perhaps too cryptic. + + * `println_err!` and `print_err!` -- less cryptic, but significantly + more awkward to type; it is the author's personal opinion that + these names will lead people to continue using `println!` for + error messages. + + * `errorln!` and possibly also `error!` -- ruled out by the + [`log` crate][log-crate] already using `error!` for something + else, IMHO. + + * `errln!` and possibly also `err!` -- `err!` is too likely to be + taken by a crate already IMHO. If we could omit the no-newline + macro, however, I rather like `errln!`. + +An [implementation][] (using `eprint(ln)!`) already exists and should +need only trivial revisions after the above decisions are made. + +# How We Teach This +[how-we-teach-this]: #how-we-teach-this + +Since these macros are exactly the same as the existing `print(ln)!` +macros but for writing to stderr, it will not be necessary to teach +how they work. + +It will, however, be necessary to add text to the reference manual and +especially to the tutorials explaining the difference between "primary +output" and "status reports", so that programemrs know _when_ to use +them. All of the existing examples and tutorials should be checked +over for cases where `println!` is being used for a status report, and +all such cases should be changed to use the new macro instead. + +# Drawbacks +[drawbacks]: #drawbacks + +The usual drawbacks of adding macros to the prelude apply. In this +case, I think the most significant concern is to choose names that are +unlikely to to conflict with existing library crates. + +# Alternatives +[alternatives]: #alternatives + +Conceivably, the ergonomics of `writeln!` could be improved to make +this unnecessary. There are three fundamental problems with that, +though: + +1. `writeln!(stderr(), ...)` is always going to be more typing than + `eprintln!(...)`. People live with `fprintf(stderr, ...)` in C, so + perhaps that's not that bad. + +1. `writeln!` returns a Result, which must be consumed; this is + appropriate for the intended core uses of `writeln!`, but means + tacking `.unwrap()` on the end of every use to print diagnostics + (if printing diagnostics fails, it is almost always the case that + there's nothing more sensible to do than crash). + +1. `writeln!(stderr(), ...)` is unaffected by `set_panic()` (just as + `writeln!(stdout(), ...)` is unaffected by `set_print()`). This is + arguably a bug. On the other hand, it is also arguably the Right Thing. + +# Unresolved questions +[unresolved]: #unresolved-questions + +See discussion above. + +[pre-rfc]: https://internals.rust-lang.org/t/extremely-pre-rfc-eprintln/4635/10 +[log-crate]: https://crates.io/crates/log +[implementation]: https://github.com/rust-lang/rust/pull/39229/files From c53d19e35690443573c6e71bbf544c7cacded701 Mon Sep 17 00:00:00 2001 From: Zack Weinberg Date: Wed, 22 Feb 2017 10:27:17 -0500 Subject: [PATCH 2/2] Revise eprintln RFC --- text/0000-eprintln.md | 157 ++++++++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 53 deletions(-) diff --git a/text/0000-eprintln.md b/text/0000-eprintln.md index d10aa8d9aa0..df249d8750d 100644 --- a/text/0000-eprintln.md +++ b/text/0000-eprintln.md @@ -11,6 +11,8 @@ This RFC proposes the addition of two macros to the global prelude, `println!`, respectively, except that they write to standard error instead of standard output. +An [implementation][] already exists. + # Motivation [motivation]: #motivation @@ -40,74 +42,121 @@ more concepts, all of the tutorial documentation for the language uses # Detailed design [design]: #detailed-design -Most of the design is already nailed down by the existing `println!` -macro. It is my intention to clone the existing definition verbatim, -changing only the name and the I/O stream written to. Anything other -than strict parallelism with `println!` would be surprising and -confusing. - -There remain two design decisions to make: - - * Should there be an `eprint!` analogous to `print!`? (That is, a - macro that writes formatted text to stderr _without_ appending a - newline.) I suspect that it will be rarely used, but I also - suspect that its absence will be surprising. Leaving out `eprint!` - might enable a better choice of name for `eprintln!` (see below). - - * What should the name(s) of the macro(s) be? There were four - candidates proposed in [the pre-RFC][pre-rfc]: - - * `eprintln!` and `eprint!` -- easy to type, and two different - people said they were already using these names in their own - code; but perhaps too cryptic. - - * `println_err!` and `print_err!` -- less cryptic, but significantly - more awkward to type; it is the author's personal opinion that - these names will lead people to continue using `println!` for - error messages. - - * `errorln!` and possibly also `error!` -- ruled out by the - [`log` crate][log-crate] already using `error!` for something - else, IMHO. - - * `errln!` and possibly also `err!` -- `err!` is too likely to be - taken by a crate already IMHO. If we could omit the no-newline - macro, however, I rather like `errln!`. - -An [implementation][] (using `eprint(ln)!`) already exists and should -need only trivial revisions after the above decisions are made. +Two macros will be added to the global prelude. `eprint!` is exactly +the same as `print!`, and `eprintln!` is exactly the same as +`println!`, except that both of them write to standard error instead +of standard output. "Standard error" is defined as "the same place +where `panic!` writes messages." In particular, using `set_panic` to +change where panic messages go will also affect `eprint!` and +`eprintln!`. + +Previous discussion has converged on agreement that both these macros +will be useful, but has not arrived at a consensus about their names. +An executive decision is necessary. It is the author's opinion that +`eprint!` and `eprintln!` have the strongest case in their favor, +being (a) almost as short as `print!` and `println!`, (b) still +visibly different from them, and (c) the names chosen by several +third-party crate authors who implemented these macros themselves for +internal use. # How We Teach This [how-we-teach-this]: #how-we-teach-this -Since these macros are exactly the same as the existing `print(ln)!` -macros but for writing to stderr, it will not be necessary to teach -how they work. - -It will, however, be necessary to add text to the reference manual and -especially to the tutorials explaining the difference between "primary -output" and "status reports", so that programemrs know _when_ to use -them. All of the existing examples and tutorials should be checked -over for cases where `println!` is being used for a status report, and -all such cases should be changed to use the new macro instead. +We will need to add text to the reference manual, and especially to +the tutorials, explaining the difference between "primary output" and +"status reports", so that programmers know when to use `println!` and +when to use `eprintln!`. All of the existing examples and tutorials +should be checked over for cases where `println!` is being used for a +status report, and all such cases should be changed to use `eprintln!` +instead; similarly for `print!`. # Drawbacks [drawbacks]: #drawbacks The usual drawbacks of adding macros to the prelude apply. In this case, I think the most significant concern is to choose names that are -unlikely to to conflict with existing library crates. +unlikely to to conflict with existing library crates' _exported_ +macros. (Conversely, _internal_ macros with the same names and +semantics demonstrate that the names chosen are appropriate.) + +The names `eprintln!` and `eprint!` are terse, differing only in a +single letter from `println!` and `print!`, and it's not obvious at a +glance what the leading `e` means. ("This is too cryptic" is the +single most frequently heard complaint from people who don't like +`eprintln!`.) However, once you do know what it means it is +reasonably memorable, and anyone who is already familiar with stdout +versus stderr is very likely to guess correctly what it means. + +There is an increased teaching burden---but that's the wrong way to +look at it. The Book and the reference manual _should have_ been +teaching the difference between "primary output" and "status reports" +all along. This is something programmers already need to know in +order to write programs that fit well into the larger ecosystem. Any +documentation that might be a new programmer's first exposure to the +concept of "standard output" has a duty to explain that there is also +"standard error", and when you should use which. # Alternatives [alternatives]: #alternatives -Conceivably, the ergonomics of `writeln!` could be improved to make -this unnecessary. There are three fundamental problems with that, -though: +It would be inappropriate to introduce printing-to-stderr macros whose +behavior did not exactly parallel the existing printing-to-stdout +macros; I will not discuss that possibility further. + +We could provide only `eprintln!`, omitting the no-newline variant. +Most _error_ messages should be one or more complete lines, so it's +not obvious that we need `eprint!`. However, standard error is also +the appropriate place to send _progress_ messages, and it is common to +want to print partial lines in progress messages, as this is a natural +way to express "a time-consuming computation is running". +[For example][progress-ex]: + +``` +Particle 0 of 200: (0.512422, 0.523495, 0.481173) ( 1184 ms) +Particle 1 of 200: (0.521386, 0.543189, 0.473058) ( 1202 ms) +Particle 2 of 200: (0.498974, 0.538118, 0.488474) ( 1146 ms) +Particle 3 of 200: (0.546846, 0.565138, 0.500004) ( 1171 ms) +Particle 4 of 200: _ +``` + +We could choose different names. Quite a few other possibilities have +been suggested in the [pre-RFC][] and [RFC][] discussions; they fall +into three broad classes: + + * `error(ln)!` and `err(ln)!` are ruled out as too likely to collide + with third-party crates. `error!` in particular is already taken + by the [`log` crate][log-crate]. + + * `println_err!`, `printlnerr!`, `errprintln!`, and several other + variants on this theme are less terse, but also more typing. It is + the author's personal opinion that minimizing additional typing + here is a Good Thing. People do live with `fprintf(stderr, ...)` + in C, but on the other hand there is a lot of sloppy C out there + that sends its error messages to stdout. I want to minimize the + friction in _using_ `eprintln!` once you already know what it means. + + It is also highly desirable to put the distinguishing label at the + _beginning_ of the macro name, as this makes the difference stand + out more when skimming code. + + * `aprintln!`, `dprintln!`, `uprintln!`, `println2!`, etc. are not + less cryptic than `eprintln!`, and the official name of standard + I/O stream 2 is "standard _error_", even though it's not just for + errors, so `e` is the best choice. + +Finally, we could think of some way to improve the ergonomics of +`writeln!` so that we don't need the new macros at all. There are +four fundamental problems with that, though: 1. `writeln!(stderr(), ...)` is always going to be more typing than - `eprintln!(...)`. People live with `fprintf(stderr, ...)` in C, so - perhaps that's not that bad. + `eprintln!(...)`. (Again, people do live with `fprintf(stderr, + ...)` in C, but again, minimizing usage friction is highly + desirable.) + +1. On a similar note, use of `writeln!` requires `use std::io::Write`, + in contrast to C where `#include ` gets you both `printf` + and `fprintf`. I am not sure how often this would be the _only_ + use of `writeln!` in complex programs, however. 1. `writeln!` returns a Result, which must be consumed; this is appropriate for the intended core uses of `writeln!`, but means @@ -124,6 +173,8 @@ though: See discussion above. -[pre-rfc]: https://internals.rust-lang.org/t/extremely-pre-rfc-eprintln/4635/10 +[pre-RFC]: https://internals.rust-lang.org/t/extremely-pre-rfc-eprintln/4635/10 +[RFC]: https://github.com/rust-lang/rfcs/pull/1869 +[progress-ex]: https://github.com/rust-lang/rfcs/pull/1869#issuecomment-274609380 [log-crate]: https://crates.io/crates/log [implementation]: https://github.com/rust-lang/rust/pull/39229/files