News for hackers. Stuff that matters.
Re:Announcing Rust 1.13 by paulddraper on Thursday November 10, 2016 @05:28PM (#12925118)
It's true.

Rust doesn't have exceptions, for many reasons. One is non-locality of exceptions. Even gotos but bounded by a function. Exceptions, however...those can take you anywhere.

But the other side, as you point out, is that exception many common patterns are much made more succient.

Re:Announcing Rust 1.13 by Arnavion on Thursday November 10, 2016 @05:53PM (#12925345)
>One is non-locality of exceptions. Even gotos but bounded by a function. Exceptions, however...those can take you anywhere.

I've never understood this argument.

Where does `try!()` take you if there's an error? To the caller. If the caller also has a `try!()`, where does that take you? To the caller's caller.

Where does `throw new FooException()` take you? To the caller. If the caller doesn't have a `catch(FooException)`, where does that take you? To the caller's caller.

Edit: Note to commenters: I'm not arguing about explicit vs implicit returns. I do agree that explicit returns are nicer to read and reduce the surface area of a language. My comment is solely for the "Exceptions can take you anywhere" sentiment.

Re:Announcing Rust 1.13 by Manishearth on Thursday November 10, 2016 @06:14PM (#12925534)
Explicit vs implicit basically. You don't get to ignore the error. If the caller does have a try, it propagates. If the caller doesn't have a catch, it propagates.

This becomes important because now your code no longer has invisible escape hatches 20 levels deep. You design your function with specific points where it may stop executing. You control when your function returns.

You can also store the error value and handle it later (Servo makes use of this pattern often -- e.g. a network request with a body of Err, which needs to be handled when we try to read the body).

> Where does `try!()` take you if there's an error? To the caller. If the caller also has a `try!()`, where does that take you? To the caller's caller.

You can say this about return, too. returns and try both take you to the caller. The caller then bubbles you up, because it explicitly returns or tries.

Re:Announcing Rust 1.13 by orthecreedence on Thursday November 10, 2016 @08:50PM (#12926706)
I LOVE this about Rust. Whenever I'm using another library, if a function I'm calling returns a Result, I know that I need to handle an error condition. With exceptions, you never really know if the code you're calling throws exceptions or not. The explicitness of "yes, this function may return an error condition" is really nice. And if you really don't give a damn about errors, you can just `.unwrap()` everything.
Re:Announcing Rust 1.13 by solidsnack9000 on Friday November 11, 2016 @01:25AM (#12928450)
I think this is the most meaningful and valuable thing about it: `Result` gives you a type-level indication of the need for error handling, but in a manner that is sufficiently polymorphic and friendly to handle that it doesn't end up like Java's checked exceptions (which could be seen as a special sub-system of the type system).
Re:Announcing Rust 1.13 by pjmlp on Friday November 11, 2016 @02:19AM (#12928671)
The problem with checked exceptions in Java is that many don't design them properly, not everything needs to extend Exception, there is a reason why RuntimeException is there since the early days.

Yes, even the JDK is to blame for misusing checked exceptions.

Re:Announcing Rust 1.13 by rvense on Friday November 11, 2016 @01:38AM (#12928497)
You can smash ahead with unwrap()s and get something that works. And then when you want to make it reliable, you know where to look. It's like the language forces me to put in those "// TODO"s my code would otherwise be full of..
Re:Announcing Rust 1.13 by bluejekyll on Thursday November 10, 2016 @06:36PM (#12925748)
There are other good comments here, but the use case where I find it most important is in lambdas. What do you do when a lambda throws an exception in the processing loop of an iterator?

  some_vec.iter().filter_map(|i| 
                   i.to_something()
                    .map_err(|e|None)
                    .map(|i|Some(i)))
                 .collect()
I think that will compile. The `to_something()` might return an error, the important thing is that there is no exception and allows for clean error handling. In this case we're just ignoring errors. With exception handling, while it's not impossible to do the exact same thing, I believe this is cleaner (and yes, there are other ways to write this).
Re:Announcing Rust 1.13 by pjmlp on Friday November 11, 2016 @02:22AM (#12928686)
C#, C++, Java, F#, OCaml, Haskell, Lisp

    try_block
        some_vec.iter().filter_map(|i| 
                           i.to_something()
                            .map_err(|e|None)
                            .map(|i|Some(i)))
                         .collect()
    catch_exception
Re:Announcing Rust 1.13 by bluejekyll on Friday November 11, 2016 @03:47AM (#12929012)
This is not correct. The to_something() is what throws the exception. In those languages, you need to put the try block inside the iterator's map lambda.

Again, I didn't say it was not possible in the other languages, I only stated that I didn't think it ends up being as clean (specifically in regards to C++, Java and C#)

Re:Announcing Rust 1.13 by gpderetta on Friday November 11, 2016 @05:05AM (#12929335)
In most languages, it is easy to write a 'try(expr, fail)' HOF that gives you the same functionality (but possibly with different performance profiles, depending on the underlying exception handling implementation).

On the other hand without non-local error handling it is harder to bail out of the outer map on a to_something error, if that's what's desired.

Re:Announcing Rust 1.13 by junke on Friday November 11, 2016 @06:42AM (#12929771)
In Lisp the "catch" part could be "handler-bind", which could invoke a restart (skip element, or use-value). This requires the called function to establish a restart, but the good news is that it removes the burden of the decision from the innermost part.
Re:Announcing Rust 1.13 by pjmlp on Friday November 11, 2016 @08:19AM (#12930289)
So what, I don't care where the error occurs.

Either the full expression is successful or not.

Re:Announcing Rust 1.13 by bluejekyll on Friday November 11, 2016 @10:04AM (#12931205)
What we are debating at this point is style. I offered something which I think is cleaner than what I see in other languages.

You don't see the point, and that's fine. Initially when Java came out with Streams I hadn't worked a lot with ETL like functions. Then while with Rust I really got it, so now I want to apply that practice to code in Java, because it simplifies many areas of code. One thing that became very clear, is that Exception handling throws a minor monkey wrench into keeping the code simple. This is why I picked the example I showed.

You don't think it's cleaner, or makes a big difference, and that's fine. I personally find it to be easier to read and create more understandable code. You can have your exceptions, but after Rust I am totally sold on the Result for errors pardigm.

Re:Announcing Rust 1.13 by pjmlp on Saturday November 12, 2016 @11:12AM (#12938535)
I don't think it is cleaner, because usually we architect our applications in logical blocks.

For example when sending a data packet over the network and it fails, I don't care if the error was in the communication, buffer, socket level or network layer, just that the packet could not be sent.

That is what we need to retry, sending a new packet, not the lower level operations.

Languages with exceptions, which I use since 1993, allow me to choose using an exception, transform it into a plain error code or monadic error. I can have it all and choose which flavour I want to use, depending on the use case.

But as you say, it is a matter of style.

Re:Announcing Rust 1.13 by paulddraper on Thursday November 10, 2016 @06:33PM (#12925714)
Except that try!() returns to the call site.

Raising an exception does not. It's akin to a `return` and `goto` all in one.

---

I don't mean to criticize Python's choice.

I think exceptions are a good decision for a high-level, dynamically typed, scripting language.

I do not think exceptions are a good decision for statically typed systems language.

Re:Announcing Rust 1.13 by Retra on Thursday November 10, 2016 @07:00PM (#12925965)
Yeah there's a good number of people who just don't want exception handling and stack unwinding code in their binaries. If Rust is going to be a usable systems language, it at least needs to placate the needs of systems developers.
Re:Announcing Rust 1.13 by tmzt on Thursday November 10, 2016 @06:16PM (#12925552)
Except it's treated as a normal return and could be written that way. There is no stack magic happening.

The return value is a Result, which is an enum and must be handled in a match or other construct, or explicitly ignored with an .ok or .expect (that may be for Option only) which result in a panic if not successful.

Re:Announcing Rust 1.13 by rads on Thursday November 10, 2016 @06:19PM (#12925582)
The argument only applies to unchecked exceptions because those can happen from anywhere without being mentioned by the calling code. It's easy to sneak through a layer of abstraction since the bubbling is implicit.

The `try!()` in this case is analogous to checked exceptions. It makes the bubbling explicit.

Re:Announcing Rust 1.13 by friendzis on Friday November 11, 2016 @10:19AM (#12931351)
In my view non-locality manifests with wrappers when you call wrapper function, which in turn calls wrappee (how do you call it?), which throws/excepts/whatevers but the wrapper does not catch it or merely logs and rethrows it: now you get return from wrappee directly to the caller. Or in other words: "Exceptions can take you anywhere".

This is a huge problem with wrappers in general that signature of wrappee becomes part of wrapper signature. Since exceptions are part of function signature they make the problem worse. And since most if not all languages do not enforce exceptions in signature there is literally no way to know what will be thrown if runtime polymorphism is used. Which results in situations where querying REST endpoint written in a language we all love to hate returns trace in the body, ooops :)

Re:Announcing Rust 1.13 by junke on Friday November 11, 2016 @11:42AM (#12932197)
In those cases I take a defensive approach.

There will be exceptions I don't know about, and that's fine. I let go the desire to fully know the set of all exceptions that can be thrown. Instead, I weaken my assumptions and code as if everything could break, and that makes code quite robust. For example, I try to arrange the code so that it is transactional, which can be easy if the approach is functional. Otherwise, places that must be protected get a catch-all handler, etc.

Re:Announcing Rust 1.13 by yazaddaruvala on Thursday November 10, 2016 @06:19PM (#12925586)
"Checked Exceptions" and the Result pattern can be translated back and forth by a machine (i.e. syntax sugar for each other).

The trick is "Exceptions" are more than just "Checked Exceptions"

Re:Announcing Rust 1.13 by Retra on Thursday November 10, 2016 @07:04PM (#12925995)
Checked exceptions probably act more like ad-hoc enums since you've got to handle each kind of exception that can be thrown, but I'm sure that could be encoded in nested results too.
Re:Announcing Rust 1.13 by yazaddaruvala on Thursday November 10, 2016 @08:08PM (#12926441)
Fair enough, CheckedExceptions are almost always "fat pointers". Meaning they are all the same size, and can be passed around equivalently.

The Rust equivalent would be something like:

`Result<_, Box<Error>>`

Re:Announcing Rust 1.13 by gpderetta on Friday November 11, 2016 @05:13AM (#12929374)
Amen, they have the same expressive power.

The thing is that exceptions do not require the manual CPS transform required to handle Result objects, which results in nicer code.

Even with syntactic sugar like do notation, the syntax is still different from 'normal' code.

Also the escape hatch to unchecked exceptions is convenient too (then again Rust has some form of it already).

Re:Announcing Rust 1.13 by zphds on Thursday November 10, 2016 @08:26PM (#12926553)
try!() is a macro. Hence all it does is take a given expression and expand the whole thing into a if-error-then-return block. I guess you say that it's an explicit return under the hood.