Live data from Hacker News

Benefits of named return values in Go

blog.minio.io

81–90 of 146 posts

Re: Benefits of named return values in Go

#81
post #74
post #67

Earlier quoted context omitted.

Are 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.g…

The Go code review document gives a brief comment about when they like and don't like to use them[1]. Personally the only time I find them to be acceptable is when you have to reference a returned value in a defer. All other usages are questionable, though this is a taste thing and is likely because of my dislike in it's usage as a way of omitting a `var` in a function body (and also the fact that naked returns are m…

I don't think naked returns are any more magical than when you just drop a variable name in there. Either way, when you read a return statement you'll want to re-read the code to find the context of that return. The only difference is with named returned the variable is declared in the function declaration rather than the body of code. So it's definitely just a preference thing there.

My coding style is to try and avoid lengthy functions - my procedural code is definitely inspired by the functional paradigm. Granted it's not always possibly to avoid a long function but for the shorter ones named returns tend to produce cleaner looking code _in my opinion_.

In any case, I will definitely take your points on board and be more mindful about when using named returns - ie think about whether they're adding to the readability or if I'm just being lazy saving myself a var declaration. Thank you for the reference article as well.

Re: Benefits of named return values in Go

#82

Earlier quoted context omitted.

Cool. Can anything execute between the end of the deferer and the start of the deferred?

Anything as from any other thread/goroutine? Yes of course, all shared data must be protected from races. If you mean in the same context then, no.

I'm thinking of it in the way that the interpreter works in JavaScript with tasks. If you setTimeout(A, 0), inside B, then after B returns, A is called, but any other tasks previously inserted into the queue are called first.

So I think that means I was asking about execution in the same context (as in memory context), unless you mean stack frame by context, in which case, I think i understand that because the caller returns the value of the deferred, they are in the same stack frame, and nothing else could insert in that frame between them. I'm not sure you know what i mean, but do i have it about right?

I don't really understand this, but i think I'm getting somewhere.

Re: Benefits of named return values in Go

#83

Earlier quoted context omitted.

Ok now I got they are in scope. About defer, could you think of it like defer makes a function into a function with multiple entry and exit points, possible to be stopped and resumed, like a coroutine?

Ehm, not sure I understood, but I'd say no. Defer is just code that is executed in reverse order upon return from a fuction. It has nothing to do with coroutines. It's a cleaner way to write the usual C syntax of "goto cleanup_x" code. Go coroutines (goroutines) are functions invoked with the "go" keyword. These cannot be stopped or resumed, but might be (possibly) executed on a different thread. In any case, they ar…

Thanks alot for explaining, I think i get it a bit better now.

Re: Benefits of named return values in Go

#84

Earlier quoted context omitted.

Is that actually returning err as the deferred return value of test? In other words, to the caller of test it appears test returns the result of the deferred?

Deferred functions don't return anything. You can modify the values that are returned by the parent function, and those will be returned as the deferred function modified them.

Ah, okay, thanks. I get it better now. Sort of like decorators in purpose, then.

Re: Benefits of named return values in Go

#85
post #71

Earlier quoted context omitted.

It increasingly does. But it's been a process. Go 1.5 was the first self-hosting release, with the Go compiler auto-translated from C to Go. But it was still fundamentally Ken's C compiler in Go syntax. Every release since (Go 1.6, Go 1.7, Go 1.8, Go 1.9) has been cleaning it up and making it more Go like and less C like. Meanwhile, the backend was also retrofitted. Go 1.7 included an SSA backend for amd64 ( https://…

Why does Go not use LLVM? Are there technical reasons to reinvent the wheel, or is it just because LLVM is Apple's pet?

I hope this won't come out harsher than I intend to, but I'm so tired to hear this expression "not reinventing the wheel" to justify using third party code. This is not what it means.

Note that there is not a single wheel that was built once in prehistory and now every human gets it lent when they need it. People build wheels everyday to fit their needs, reusing the concept of wheel, that is, knowing that a circular object allows for smooth movements with less friction. The analogy in software development means that you've better know of designs that help you solve your problem, not that you should blindly use code built by someone else to bypass the whole problem solving. This is basically trying to use a bicycle wheel for everything. This may work well on an other bicycle, not on a car.

Re: Benefits of named return values in Go

#86
post #73

Earlier quoted context omitted.

Deferred functions don't return anything. You can modify the values that are returned by the parent function, and those will be returned as the deferred function modified them.

Yep, and one cannot simulate that with local variables. In Go "return v" copies v into the return location before calling the deferred code. If that location is not named, the deferred function has no way to change it, see https://play.golang.org/p/Opg4XI08P7

I got it. That's a neat example. Do you have to define that deferred function inside the caller, in order to reference the name? Or can you factor out deferred functions to be used by various callers and pass in the return names for them to modify?

Re: Benefits of named return values in Go

#87

Earlier quoted context omitted.

Just for what it's worth, the way I'd fix that problem in the compiler would be to implement dead store elimination via global value numbering. With trivial alias analysis, the compiler would be able to detect that the result of the "duffzero" instruction (which I assume is a memset) is always killed by the "duffcopy" instructions and would eliminate it. See this article for how it's done in LLVM: http://blog.llvm.or…

Not my area of expertise and it is yours, but if you eliminate a zero instruction before a copy instruction how can you be sure that doesn't affect other threads? var x Int // Pass x to a thread by reference x = 0 time.Sleep(1000) x = 1

The answer to that one would be to embed thread-safety in the type system, aka. Rust.

For languages with less sophisticated type systems you get a choice between inefficiency (Go), or complicated rules which state that the programmer is wrong for coding that way (C).

Re: Benefits of named return values in Go

#88
post #67
post #32

Earlier quoted context omitted.

For 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". :)

Are 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.g…

Eh, I've heard this a few times; I thought once from Francesc Campoy, but maybe I'm wrong? I don't feel strongly about them, I was just repeating what I heard.

The rationale was that they can make code hard to follow, especially when one of the returns is err and you have other errs in your function.

Lastly, to be clear, I didn't mean to imply they were frowned upon in all cases; only that you should prefer normal returns unless you have a compelling reason.

Re: Benefits of named return values in Go

#89

Earlier quoted context omitted.

Just for what it's worth, the way I'd fix that problem in the compiler would be to implement dead store elimination via global value numbering. With trivial alias analysis, the compiler would be able to detect that the result of the "duffzero" instruction (which I assume is a memset) is always killed by the "duffcopy" instructions and would eliminate it. See this article for how it's done in LLVM: http://blog.llvm.or…

Not my area of expertise and it is yours, but if you eliminate a zero instruction before a copy instruction how can you be sure that doesn't affect other threads? var x Int // Pass x to a thread by reference x = 0 time.Sleep(1000) x = 1

How can you be sure that the other threads ever see it in time? they might be suspended for a whole second because a HDD needs to spin up or something like that.

So threads never seeing the value is already a valid outcome, so the compiler might as well always do that.

Re: Benefits of named return values in Go

#90

Earlier quoted context omitted.

Anything as from any other thread/goroutine? Yes of course, all shared data must be protected from races. If you mean in the same context then, no.

I'm thinking of it in the way that the interpreter works in JavaScript with tasks. If you setTimeout(A, 0), inside B, then after B returns, A is called, but any other tasks previously inserted into the queue are called first. So I think that means I was asking about execution in the same context (as in memory context), unless you mean stack frame by context, in which case, I think i understand that because the caller…

It's much simpler than that. It's just a way of defining cleanup functions without having a language-level concept of destructors. Here's an example: https://gobyexample.com/defer.

This simple example should explain everything:

    package main

    import "fmt"

    func function1() {
        defer fmt.Println("function1: defer a")
        fmt.Println("function1: inside")
        defer fmt.Println("function1: defer b")
    }

    func main() {
        fmt.Println("main: before function1")
        function1()
        fmt.Println("main: after function1")
    }
Here is the output:

    main: before function1
    function1: inside
    function1: defer b
    function1: defer a
    main: after function1
All defers run in LIFO order at the point when a function returns before the function returns execution back to the caller.
Post reply on HN