13 ms·
Benefits of named return values in Go
- anonymous7777 9y agoExtremely overrated Golang.
- piinbinary 9y agoI don't know all that much about compilers, but I'm slightly surprised that Go doesn't automatically optimize the first into being the same as the second.
- asdfaoeu 9y agoSounds like an explicit version of https://en.wikipedia.org/wiki/Return_value_optimization https://en.wikipedia.org/wiki/Return_value_optimization .
- dom0 9y agoNo, RVO is about the compiler silently moving the on-stack location of the "returned" object into the callers frame.
- patrickxb 9y agoOr you could return a pointer...
- deleted 9y ago[deleted]
- deleted 9y ago[deleted]
- edsrzf 9y agoI normally hate it when people immediately trot out the old "premature optimization" quote, but it really applies here. Please don't go around naming all your returns just because today's compiler happens to generate better code with them. This is a compiler issue that I'm confident will be fixed one day, especially if you do the right thing and file an issue. But by all means, if you're profiling and your inner loops are actually slowed down by this, then make the change. And add a comment so that someone might be able to change it back some day when the compiler's improved.
- obstinate 9y agoI dunno. A 30% code size win is non-trivial. I'm all for filing an issue first and seeing how likely it is that there is uptake from the dev team and desire to fix the problem. However, if no fix is forthcoming . . . code size has fairly well known effects on performance.
- gizmo686 9y agoA 30% code size reduction in code that does little other than construct and return a value. I have certainly seen individual functions where this is the case, but across an entire program, you will not get anywhere near 30% size reduction. Having said that, this is certainly something that should be fixed in the compiler. On a related note, in the final assembly, the compiler could also have optimized the 4 RETs into 1, then optimized away all of the conditionals, turning the sample code into the equivilent of "return objectInfo()"). Of course, in a real example, these optimizations would not be possible; but they do show that these reduced cases are not the best way of benchmarking performance.
- lobster_johnson 9y agoFixing it in the compiler will fix it for everyone, however, which is a big argument for fixing it upstream.
- pjmlp 9y agoUnless it actually impacts the use case of the application, and has been confirmed by a profiler that is indeed the case, it is just cargo cult optimizations.
- 19eightyfour 9y agoI was going to post "But JS has this: return { named_var_1, named_var_2 } const { named_var_1, named_var_2 } = f() But then I read the article. I don't know really what is going on in this article on a quick reading, but it builds by existing sense that go is a really cool language that I ought to get into. I have no idea if anyone wants to help me understand, but in the last part where he changes to use a named return parameter, he says, you may also "enjoy the cleaner look of the source code, too". To me the only difference I see is the return name, to the right of the existing function signature. What was I missing that makes this actually cleaner?
- piinbinary 9y agoIt allows you to assign to the "return variables" inside the function, then just return without naming all the returned values at every return. e.g. https://play.golang.org/p/gdac5QR-wW https://play.golang.org/p/gdac5QR-wW This can also be used to return the default value for a type by never assigning anything to the return value.
- 19eightyfour 9y agoOkay, I think I get it. The format / syntax is pretty easy, simple and clean. I like it. I added a new function that takes two params and returns param, so I think I get this naming the returns now. https://play.golang.org/p/7bfiSqKY_w https://play.golang.org/p/7bfiSqKY_w I was also looking for a "spread" / ... operator, but couldn't see a way to do that.
- alexhornbake 9y agoNaming the return value in the function signature will both allocate the named variable, as well as make it the default variable to return. Returning a new unnamed variable for every return will cause all of the unnamed variables to be allocated. It's interesting, I don't use this feature of the language... my default way to write this would have been: func NoNamedReturnParams(i int) (*objectInfo) { obj := &objectInfo{} if i == 1 { // Do one thing return obj } if i == 2 { // Do another thing return obj } if i == 3 { // Do one more thing still return obj } // Normal return return obj }
- pbnjay 9y agoThis clouds up your godoc with unnecessary names though. Why not just initialize a default value at the top of your function? You don't get the bare returns but it's a lot cleaner.
- artursapek 9y agoThis is one of the few places Go fails to avoid the "multiple ways of doing the same thing" problem that many languages are overflowing with. It adds cognitive overhead and creates needless debate and afaik avoiding this has been one of the Go team's guiding principles.
- deleted 9y ago[deleted]
- notheguyouthink 9y agoDoes it? Names are documentation no? Eg, that's the one thing I actually like about named return values. In my perfect world, we would have named return values by default, so then we could use them to document the return values, but never empty returns. Empty returns are the only problem with named return values Imo, as it's easy to accidentally slip values into the return that you didn't intend. Though, I had always wondered about returning copies like Foo{} on every return.. guess I know now. edit: As an aside, I should not dismiss your cluttering comment. I did so, because I feel it's unrelated to this exact topic. If named return values, something which by it's very nature conveys additional information, is cluttering the godocs, then that should be a bug, and filed/fixed accordingly. Imo.
- pbnjay 9y agoThis post specifically used very short uninformative names though, which was more to my point. If you're not going to use informative names, it shouldn't be part of the signature, especially when its barely any extra typing to do the same within the function body.
- 9y ago
- voidlogic 9y ago>In fact we are also investigating if we can develop a little utility to help or automate this process. go fix? Also, it seems like this is begging for a compiler optimization that will make this all obsolete...
- deleted 9y ago[deleted]
- azinman2 9y agoThis is a neat trick. But when I look at their examples it seems more like err conditions rather than normal behavior for the early return, in which case they should add the error into the return tuple?
- donatj 9y agoFor me, the biggest reason I use named parameters is when something I'm ’defer’ring can fail. You can set your err return value within your defer only if it's named. I went into reading this expecting that use case but was saddened it was not mentioned. The use case the author does give is kind of meh, I find it to be harder to understand what is going on than returning as usual, and that's worse than the slight supposed compiler gain is worth.
- lobster_johnson 9y agoThis is really a Go wart, in my opinion. Go forces you into this pattern, because there's no other way if you want to use defers. So while it's true that it's a legit use of named return values, it's an unfortunate one, too.
- donatj 9y agoI feel like a lot of deferred errors go unhandled because inexperienced devs don't know that they can.
- weberc2 9y agoI don't do it, but mostly because all I would be doing is logging the error, and if some day it takes me a little longer to root cause some odd file close error, I'll still come out ahead.
- dullgiulio 9y agoThat's one big reason why naked returns are frowned upon. It's much easier to read code where there is a clean "return nil, fmt.Errorf(...)".
- 013a 9y agoI'm not the most proficient Go developer, but aren't the two examples doing two different things? In the first way, each branch allocates its own objectInfo struct. In the second way, all the branch exits share the same single objectInfo struct which I assume is implicitly initialized to the zero value by the compiler. Functionally these are the same, but couldn't you achieve the same results without named return values by just allocating a single objectInfo at the top of the function and returning it?
- geekme 9y agoYes it will work.
- fwessels 9y agoYes, you can, but just naming the return variable takes care of this.
- deleted 9y ago[deleted]
- blahedo 9y agoThis was my observation as well, but even more importantly, in the given examples they're naming a value to get this benefit, but then never actually referring to the value by name. This is contorted at best.
- Gonzih 9y agoYes, you are correct. Doing var return MyStruct {} at the beginning of the function would do exactly the same thing as naming return value.
- KirinDave 9y agoI look at these result functions and all I can think is, "Wow, it must be really delightful when you get a zero record back and what to know why your parser has a bug." I really hope these are didactic in nature and not a representation of industry best practice. Why not just declare a record in scope and return that? Why rely on a sneaky little piece of syntax tucked away in the return value? Aren't gophers supposed to be all about a regular language with few surprises?
- weberc2 9y agoFor the most part named returns are frowned upon, and while it is admittedly an oddity in the language, having one oddity is still achieving the stated goal of "few surprises". :)
- laumars 9y agoAre they actually frowned upon? I've been writing Go for a few years now and while I acknowledge there will be blind spots in my knowledge, this is still the first I've heard against using that language feature. In fact named returns appear all over the Go source code itself[0] and the Effective Go section on named returns[1] seems positive about their usage. [0] https://github.com/golang/go/blob/master/src/os/file.go https://github.com/golang/go/blob/master/src/os/file.go (file taken at random) [1] https://golang.org/doc/effective_go.html#named-results https://golang.org/doc/effective_go.html#named-results Personally I use named returns heavily because it helps document what each return is used for. Which is particularly useful in IDEs like IntelliJ where you get the tooltip popup with the function parameters and returns. But frankly I find named returns just as useful when reading back old code on GitHub or even just in vi. I also notice I'm not the only one who uses them in production code. Here are some popular packages by other talented Go developers (all .go files picked at random)[2][3]: [2] https://github.com/kr/pty/blob/master/util.go https://github.com/kr/pty/blob/master/util.go (this package creates UNIX PTY files) [3] https://github.com/chzyer/readline/blob/master/operation.go https://github.com/chzyer/readline/blob/master/operation.go (readline package used by cockroachdb and Netflix) In fact of the packages I checked, I could only find one package that didn't make heavy use of named returns[4] [4] https://github.com/go-sql-driver/mysql/ https://github.com/go-sql-driver/mysql/ So if they are frowned upon, I'd be interested to know more about who and why.
- pfarnsworth 9y agoI actually hate named return values. I'm relatively new to Go, but I like some of the ideas about it. Named return values is something about Go that I despise because it is absolutely misused in my opinion. For such an opinionated language, with some opinions that I don't agree with, this is one that I vehemently disagree with. It makes the code a lot harder to read, and you need to keep more memorized "magic" in your head. For example, you can't just look at the return values to figure out what a function returns, now you need to look to see if the return values are named, and then trace through that. I very much prefer explicit code as opposed to implicit code, especially on a day-to-day basis. It just makes my life a lot easier in the long run.
- ramenmeal 9y agoIts a good point. Especially since one of go's mantra's is simplicity.
- nickcw 9y agoI think you are talking about bare returns, which are somewhat orthogonal to named return values. You can use non-bare returns with named return values, just mention the names in the return statement. This compiles to the same code as the bare return. I'm a bit ambivalent about bare returns, but named return values are useful, especially in the presence of defer statements.
- im_dario 9y agoI feel the opposite way. I always use named return values because I feel the code is less cluttered and less magic. I just look at the first line of a function and I know what variables are used as return. I think they are pretty explicit and simpler, because there are defined in a single point of our code. Anyway, we probably have different backgrounds :) Although it wasn't your point, what I don't like is mixing bare and named return. I think it's better to stick to one style or the other.
- mioelnir 9y agoI very, very rarely use named return values. My usual go-to problem is when one of the return values is an `error` that I want to set inside `recover()`.
- frou_dh 9y agoSurprised to find this not mentioned in the article and buried in comments. It's the first thing that comes to my mind as not merely a benefit, but an essential role played by the named returns.
- hota_mazi 9y agoI'm not familiar with Go and I'm confused by the fact that even though the returned variable is named, e.g. "ch", that name is never mentioned in the function body. What am I missing?
- dullgiulio 9y agoNaked returns. Basically, you just write "return" and that returns the values declared as named return parameters. If they are never mentioned in the body you probably have a return of the default nil value for the named return variables. But then at least one return is not naked and returns some value, discarding the named one. I think it's a bad use of named returns.
- masklinn 9y agoNothing. The named return value is default-initialised (zero-initialised) before the function body (you don't have to assign to it, you can just modify it in place), and if you don't explicitly return a different value it will be returned regardless of you using it or not.
- jlebrech 9y agothis helps with legibility I think, you can see by the function definition that you have return variables: a,b,c and don't have to scan all the returns to see which order things are in.
- cyphar 9y agoThis is possibly one of the worst reasons to use named return values. To be honest, I've always disliked them because they make `return` magical since you now have to read through the whole function twice to make sure that you know what is actually being returned. Luckily you can do explicit returns with named returns, but then it becomes confusing if you have non-explicit returns in the same function. In my mind there are only two reasons to use named returns. The first is when writing interfaces, and you want to document the returned values in a nicer way (in addition to the godoc). This is mostly a taste thing and is not required. However, the second one is required by the language and that is deferred function calls that interact with the return value of a function. If you want to have a deferred function call that does some cleanup but has to check the error value of the function (for example a cleanup that only runs if an error occurred) then you have to use named return values. If you want to have a deferred function that changes the return value of the main function then you also need named return values. This is a fairly annoying requirement of Go, but I understand why they felt it was necessary to do things that way to make it feel more explicit. Other than that, in general named returns make functions harder to read (in my opinion). In a similar vein, I really don't like Ruby's or Rust's implicit returns (though at least Rust is less magical about it).
- y4m4b4 9y agohttps://github.com/golang/go/issues/20859#issuecomment-312208678 https://github.com/golang/go/issues/20859#issuecomment-31220...
- zaphar 9y agoThis isn't necessarily an argument for named return values. It's an argument for declaring your return value up front and even then it's really just an argument for Go to do a better job in the compiler here. Named return values are one way to declare your return value up front but you could just as easily have had a oi := objectInfo{} line at the top of your function too with the same effect.
- camgunz 9y agoI went this route a little for a side project because it's natural to pass up errors via `err` this way, but I found out you have to have a naked return at the end of your function no matter what. I can't at all think of a reason for this; is there one?
- andreasgonewild 9y agoI feel like it was a mistake to allow mixing the two styles inside the same function. I ran into enough issues with non-initialized returns during my adventures in Go to stop using named return parameters completely. To me, this feature has the same appeal as JavaScript's optional semicolons; or Pascals with-statements; convenience at the price of reliability and correctness.
- kazinator 9y agoI don't understand, through these examples, why the introduction of a name is necessary in order that an empty return statement be used. The name doesn't even appear in the function. A name that is declared but not subsequently mentioned serves no purpose (or some side purpose/hack). In the "NoNamedReturnValue" variant, the return type is still declared. The compiler could, from that type alone, infer that "return" means "return a representative default instance of that type". That is to say, why can't Go programmers just have this: // oi name removed: func NamedReturnParams(i int) (/* oi */ objectInfo) { if i == 1 { // Do one thing return // wee, allow this anyway! } if i == 2 { // Do another thing return } if i == 3 { // Do one more thing still return } // Normal return return } Also, why can't the compiler just optimize away the "return Objectinfo {}" statements down to "return", if those really are equivalent.
- pklausler 9y agoLeaving aside any quick benefits in code size or run time, named return values might make code more readable. Fortran functions have always (since Fortran II anyway) used named result values; so did Pascal. Since you can now pick the name of the result value variable in Fortran, I find it a useful convention to just always put RESULT(RESULT) on my functions.
- optimuspaul 9y agoThese don't represent a real world application at all. I'm not convinced this is anything more than a compiler optimization for a function that is essentially a noop.
- Katastrophial 9y agoI like named returns and I like efficiency, nice! An argument against named returns is that things become harder to track in longer functions or functions with a lot of branching, but if you're using gometalinter you'll be warned about both of those things anyway, so I don't see the concern there.