Hacker News new | ask | show | jobs
by ajanuary 4154 days ago
The issue is it makes it difficult to notice that a function might return when scanning through a function. Especially as it's in places that are looking for a value (e.g. assignment).

At the moment it's just return and try!, but people look to the standard library for what is acceptable. When the standard library contains a macro that can return, people will write their own macros that return. It could potentially be half a dozen different macros you need to keep in your head.

Personally, I go back and forth on it. Hopefully it will turn out fine.

2 comments

> The issue is it makes it difficult to notice that a function might return when scanning through a function.

What other solutions are there? The only other approach to error handling I've seen is exceptions (e.g., C++, Java, C#, JS…), and if you don't like `try!` because it is a "hidden return", you certainly won't like exceptions. At least with Rust's macros, I know that in the absence of one, there is no return; in the presence, there might be. Exceptions in most languages make no guarantee.

It's a trade off. For the trouble of exceptions I get nice benefits like stack traces (though there are proposals to add stack traces to Rusts error handling). There are times when I really like exceptions, and times (such as trying to trace through an execution path) that I'm irritated by them.

What other solutions are there? Well, you could make the return explicit:

    let z = try!(x / y, onerror = return)
There are obvious downsides, such as added verbosity, and the need to figure out keyword arguments in macros, and do you allow access to the error value etc. but at least I can grep for/highlight "return". It also makes the meaning of the slightly confusingly named "try" more obvious (again, I'm vaguely aware of proposals that would change the name).

I think ultimately try! is a good thing, but I don't think it's trivially "a very appropriate use of macros". It's a considered use given some difficult trade-offs.

Kotlin has a notion of function inlining with the return statement being inlined too. This means you can do, e.g.:

list.forEach { if (it == something) return; }

and you aren't returning from the code block, but the enclosing function. Yet behind the scenes forEach is being expanded by the compiler into a regular imperative for loop, in a macro-like way.

I think people get used to it, especially if it's a part of the standard libs. C++ <iostream> overloads the bitshift operator and nobody seems to mind.