Hacker News new | past | comments | ask | show | jobs | submit login

Naming 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
  }



I had a bit of a play around with it. It's pretty powerful being able to see <func name> <params type> <return type> in one line ( or possibly multi line )

It really hammers home the concept that a function is a transformation, and of what into what. And I think this syntax would probably encourage pure functions. And it's so useful to allocate the return in the top line. I really like go.


I'm fairly sure that almost every statically typed language has function definitions like that. C, C++, Java, Go, Rust and so on all have that style of function declaration.

Named returns are a Go thing mainly because of defer.


How does defer interact with named returns?


As one would expect. The deferred code runs after the normal code in the function has ended, and before the calling code has resumed. Named return values are in scope and can be read and modified.

Try it and see: https://play.golang.org/p/1ozFWDj15a


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.


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.


Got it thanks a lot, that's very clear.


You can easily access and modify the named return variables from a deferred function body.

If you had a variable declared in a block just before return, then you return it, you have no way to modify it in defer.

I personally don't like this style of code, but I can see some uses.


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 are not guaranteed to be executed immediately in the normal flow of the code.


>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.

I'd say more like the equivalent of a "finally" clause (or more) for your whole function.

Though not sure about the "executed in reverse order part" -- what's "in reverse order" about Defer? Except if you mean that multiple defers get executed "last seen first"...


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


defer can refer to and change the return values. This is useful, for example, when the deferred code can trigger an error that one wants to return, https://play.golang.org/p/MBmy9OocAG


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.


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?


You can pass a pointer to the named return values to a function defined outside, like in https://play.golang.org/p/5jBKcUCj8C .


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


What language that has static types doesn't have the func, params, and return type in one line?


Some HM language can infer types at the toplevel so you may not have an explicit signature at all, although that's generally frowned upon:

    add1 = map (+1)
OTOH they also split the signature and function "header" so that you don't need to remove the "noise" to get the bare signature e.g.

    add1 :: (Num a) => [a] -> [a]
    add1 = map (+1)


C++ ends up with three forms (of course it does):

Normal C style declaration

    int foo() { return 2; }
Trailing return type. Useful when the return type depends on the parameter types, or is inside the namespace of the function

    auto foo() -> int { return 2; }
Automated type deduction. Increasingly the choice when possible

    auto foo() { return 2; }


K&R C had implied type of 'int' for both arguments and return value, and didn't list the types of arguments on the line specifying function name, argument names and return type:

  int foo(x,y)
  {
     return baz(x,y);
  }

  bar(x,y,z)
  short x;
  int z;
  {
     return x+y+z;
  }

  baz(x,y,z)
  {
     return 42;
  }




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: