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

Reply via email to