Skip to content

Commit

Permalink
Revise eprintln RFC
Browse files Browse the repository at this point in the history
  • Loading branch information
zackw committed Feb 22, 2017
1 parent f62c6d7 commit c53d19e
Showing 1 changed file with 104 additions and 53 deletions.
157 changes: 104 additions & 53 deletions text/0000-eprintln.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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 <stdio.h>` 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
Expand All @@ -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

0 comments on commit c53d19e

Please sign in to comment.