Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

I absolutely agree about the multiple return values in general.

Unfortunately the discussion around N2289 is more about providing something a lot like exceptions for both C and C++ but without all the exception problems.

The proposal really has some ugly corners related to how one actually handles the errors. You can read the discussion here:

https://www.reddit.com/r/cpp/comments/9owiju/exceptions_may_...



sort by: page size:

Multiple return values are useful in other ways; do you see them as a problem in general, or only error cases?

You can return (string, error), but then what? You can't send them over a chan (string, error) or append them to a [](string, error). You can work around this by defining a bunch of struct types, but that's exactly what multiple return values was trying to avoid, and making a human being read and write that boilerplate is far more expensive than having the compiler supply it.

My hot take here is that multiple return values are a conceptually complex way of handling tuple return values.

The C API could do this, it just doesn’t.


> It's a bad idea for the same reason that having a function that returns back multiple values is a bad idea.

Every single language that lacks multiple return values has some kind of hack to compensate, like varying parameters, that breaks encapsulation and makes code inconsistent.


The error handling proposal adds multiple return values- not as records/tuples, but as a sum type so only one can be returned from any given call.

This could certainly be made more general, or just done via tagged unions (the way multiple return values can be done via structs), but there are massive advantages to be had by building it into the language and calling convention.

The implementation can be far more efficient than any of a) the standard `int f(ret *out)` idiom, b) using errno, or c) some sort of `struct my_tagged_union f()` (which nobody uses anyway because it's a huge pain syntactically). In addition to being cheaper, the proposed built-in version of (c) is also syntactically simple enough to become a standard, shared mechanism.


Right, I jumped ahead; multiple return values is the how Go returns error values, it's not error handling itself. I don't like exceptions much either, but sum types / ADTs are just so nice for this sort of thing.

Various C/C++ programming safety standards (such as MISRA C), specify a single return only.

[0] is probably the best defense of the position I could find quickly, but the example used is convoluted, and very much "look what happens when you allow multiple returns".

I personally like to use early returns as guard statements when I'm doing precondition checking or on errors.

[0] https://spin.atomicobject.com/2011/07/26/in-defence-of-misra...


One of Go's other design decisions is signalling failures by returning errors. Multiple return values makes it possible to do that and also return a real value.

If you didn't have multiple return values, you would have to do something ugly: C-style pseudo-return via a passed-in pointer, or returning an error or a value as an interface{} and requiring the caller to type-switch on it. Or add a special case to the language to somehow allow an error return alongside a normal return. Multiple return makes this straightforward and uniform.


There are times when Go (et al) style multiple return values would better serve a function than exceptions.

For example, a function to parse a string into a number should return both a number-reference, and a flag of some kind (or maybe just an Option). Crap input should not generate an exception like out-of-memory or an I/O error. Trying to dereference the number w/out checking to see if the string was parseable COULD generate a not-mandatory-to-check exception, though.

I guess there is a certain amount of tension between the idea that functions/methods should document the kind of errors/exceptions they might have, and between the annoyance that is all the crappy do-nothing catch clauses that turn around and re-vomit wrapper exceptions that ultimately land in a "well, it didn't work" log message and punt, rather than really "handling" the original exception in any way, shape or form.


I'm not sure how, C doesn't have multiple return values either. I also don't really understand the second part. How is that any worse than the current situation of not checking the error side and reading an undefined (or whatever it is?) value?

Multiple return has two distinct use cases - to return multiple actual values, or to return an error status along with an actual value. Many languages can handle both cases by making it easy to create and return a list/tuple/whatever, and that satisfies both cases well enough for me. Others provide Optional or Error types which satisfy only the second case but do so in an even more concise way (no packing and unpacking of those lists/tuples/whatever). Zig is in that category, with some additional twists like errdefer and how null is handled. I'm not 100% sure I'd make the choices they did, but I think those sections of the Zig documentation are well worth reading to spur more thoughts about alternatives to C-style single return or exceptions.

https://ziglang.org/documentation/master/#Errors https://ziglang.org/documentation/master/#Optionals


You wrote: <<multiple return paths>>

From experience, I have never once heard anyone from higher-level languages (above C & C++) complain about "multiple return paths". That said, for C, I definitely understand the need for reduced return paths due to lack of auto-free / destructor mechanics.


I think I'd want more than multiple return values. I'd also want syntactic sugar to pass errors through to callers, or alternatively, ignore errors and skip future processing and have the error be the result of the future processing.

These things are typically done by exceptions and monads; the "zig" language has some interesting syntax around this kind of thing as well.

But just multiple return values isn't enough for me - too much "if error return error" noise for my taste.


As if that ever was the problem with C.

Multiple return values could have been way more handy but even that would be a though sell.


Because multiple return values for handling errors is a strictly inferior and error prone way for dealing with the matter.

Multiple return points are a signal to me that I'm usually (though not always) trying to encapsulate more functionality into a function than I should, or I'm using returns when it might be more semantically correct to use exceptions. I'm a big fan of the "each function does exactly one thing" rule of thumb, and when I start returning all over the place, it usually means that I'm starting to violate that rule. Except in the simple cases, multiple return statements usually means that I've got a function that's composed of multiple micro-functions, and can/should be refactored. Guard statements are an obvious exception.

Sometimes you can't avoid it, especially if you're working with duck typing functions (which are a whole 'nother discussion), and it's not a hard-and-fast rule that multiple returns are evil. There are a lot of legitimate reasons to use them - performance being the primary one - and I would absolutely take them over an 8-deep nested if structure. But, they're often a warning sign that I'm putting too many eggs in that one basket.


That's cool, thanks for the link! Hadn't considered that multiple return values could cover this case

Having a single return point is a pretty good idea in C. In other programming languages, probably not.

> I've been taught by some pretty experienced engineers that in terms of readability, multiple return statements are a bad idea

They were wrong and probably not as experienced as they/you thought. Guard clauses make code simpler and more understandable.

next

Legal | privacy