On Feb 19, 2014, at 2:34 PM, Lee Braiden <[email protected]> wrote:
>> Then we could introduce a new struct to wrap any Reader that translates
>> non-EOF errors into EOF specifically to let you say “I really don’t care
>> about failure”.
>
> It sounds like a very specific way to handle a very general problem. People
> like (modern, complete) scripting languages because they handle this sort of
> intricacy in elegant, ways, not because they gloss over it and make
> half-baked programs that don't handle errors. It's just that you can, say,
> handle IOErrors in one step, at the top of your script, except for one
> particular issue that you know how to recover from, six levels into the call
> stack. Exceptions (so long as there isn't a lot of boilerplate around them)
> let you do that, easily. Rust needs a similarly generic approach to
> propagating errors and handling them five levels up, whether that's
> exceptions or fails (I don't think they currently are flexible enough), or
> monads, or something else.
In my experience, exceptions are actually a very inelegant way to handle this
problem. The code 5 levels higher that catches the exception doesn’t have
enough information about the problem in order to recover. Maybe it just
discards the entire computation, or perhaps restarts it. But it can’t recover
and continue.
We already tried conditions for this, which do let you recover and continue,
except that turned out to be a dismal failure. Code that didn’t touch
conditions were basically just hoping nothing went wrong, and would fail!() if
it did. Code that did try to handle errors was very verbose because conditions
were a PITA to work with.
As for what we’re talking about here. lines() is fairly unique right now in its
discarding of errors. I can’t think of another example offhand that will
discard errors. As I said before, I believe that .lines() exists to facilitate
I/O handling in a fashion similar to scripting languages, primarily because one
of the basic things people try to do with new languages is read from stdin and
handle the input, and it’s great if we can say our solution to that is:
fn main() {
for line in io::stdin().lines() {
print!(“received: {}”, line);
}
}
It’s a lot more confusing and off-putting if our example looks like
fn main() {
for line in io::stdin().lines() {
match line {
Ok(line) => print!(“received: {}”, line),
Err(e) => {
println!(“error: {}”, e);
break;
}
}
}
or alternatively
fn main() {
for line in io::stdin().lines() {
let line = line.unwrap(); // new user says “what is .unwrap()?” and is
still not handling errors here
print!(“received: {}”, line);
}
}
Note that we can’t even use try!() (née if_ok!()) here because main() doesn’t
return an IoResult.
The other thing to consider is that StrSlice also exposes a .lines() method and
it may be confusing to have two .lines() methods that yield different types.
Given that, the only reasonable solutions appear to be:
1. Keep the current behavior. .lines() already documents its behavior; anyone
who cares about errors should use .read_line() in a loop
2. Change .lines() to fail!() on a non-EOF error. Introduce a new wrapper type
IgnoreErrReader (name suggestions welcome!) that translates all errors into
EOF. Now the original sample code will fail!() on a non-EOF error, and there’s
a defined way of turning it back into the version that ignores errors for
people who legitimately want that. This could be exposed as a default method on
Reader called .ignoring_errors() that consumes self and returns the new wrapper.
3. Keep .lines() as-is and add the wrapper struct that fail!()s on errors. This
doesn’t make a lot of sense to me because the struct would only ever be used
with .lines(), and therefore this seems worse than:
4. Change .lines() to fail!() on errors and add a new method
.lines_ignoring_errs() that behaves the way .lines() does today. That’s kind of
verbose though, and is a specialized form of suggestion #2 (and therefore less
useful).
5. Remove .lines() entirely and live with the uglier way of reading stdin that
will put off new users.
6. Add some way to retrieve the ignored error after the fact. This would
require uglifying the Buffer trait to have .err() and .set_err() methods, as
well as expanding all the implementors to provide a field to store that
information.
I’m in favor of solutions #1 or #2._______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev