The deferred call's arguments are evaluated immediately - go

In A Tour of Go is written:
The deferred call's arguments are evaluated immediately, but the
function call is not executed until the surrounding function returns.
I have difficulty in understanding the first part of the quote. What is called immediately?
func def(s string) func() {
fmt.Println("tier up")
fmt.Println(s)
return func(){ fmt.Println("clean up") }
}
func main() {
defer def("defered line")()
fmt.Println("main")
}
//Output:
//tier up
//defered line
//main
//clean up
https://play.golang.org/p/Av3mAEXxA4R
What is deferred here and what is evaluated immediately?

To learn how defer and evaluations work, first let's look at the Spec: defer statements:
Each time a "defer" statement executes, the function value and parameters to the call are evaluated as usual and saved anew but the actual function is not invoked.
Both the function value (whose call is deferred) and its parameters are evaluated. But the deferred function is not yet called.
Let's iterate toward your example with small steps:
defer f("a")
In this case the function value is evaluated (which will be f), and the parameters are evaluated, which is a constant, so that's gonna be "a".
Next step:
defer f(g("a"))
In this case the function value is evaluated (which will be f), and the parameters are evaluated, which means g will be called with "a" (because g's return value is the param to f).
Next step:
defer f()()
This is valid if the f function returns a function. The function value will be evaluated (which means f will be called!) but its return value will not be called, that is what will be deferred.
defer f(g())()
In this case the deferred function is the return value of f, so to evaluate the deferred function value, f must be called, and to do that g must be called prior. The return value of f will be deferred (not called).
Back to your example:
defer def("defered line")()
The function value is evaluated, which is the return value of def, so def is called. The return value of def will be deferred. Its parameters are evaluated, but actually it has no parameters.
So logically this is what happens:
The deferred function value is evaluated, which requires that:
def function must be called, which requires that:
Params to def are evaluated, it's a constant: "defered line"
Parameters to the deferred function are evaluated; since there are no params to it, this step is done.
This is what happens sequentially if we lay out the above structure:
Param to def is evaluated: it's a constant "defered line"
def is called which prints tier up and its argument: defered line
The return value of def is not called, that is what's deferred.
Function main prints: main
Function main returns, so deferred functions are called now. The deferred function prints clean up

The deferred call's arguments are evaluated immediately, but the
function call is not executed until the surrounding function returns.
Above sentence means the deferred function arguments are evaluated at the line where they are deferred but the function will run after the surrounding function which is main returns.
A defer statement pushes a function call onto a list. The list of
saved calls is executed after the surrounding function returns. Defer
is commonly used to simplify functions that perform various clean-up
actions.
Deferred function calls are executed in Last In First Out order after the surrounding function returns.
Deferred functions may read and assign to the returning function's named return values.
The above line clearly states that it will return the value to the main function.
For eg:-
func c() (i int) {
defer func() { i++ }()
return 1
}
Above function will return 2 value rather than 1. That's why this line
return func(){ fmt.Println("clean up") }
will be called in the last.
For more information on defer. Please read golang blog for defer

It will be more clear if you change the argument you're passing to def(string) from a string literal (evaluated at compile time) to something more interesting, say:
func bar(s sting) string {
return s + " bar "
}
func main() {
defer def(bar(os.Args[1]) + "defered line")()
fmt.Println("main")
}
When the defer def(bar(os.Args[1]) + "defered line")() statement
is executed, the argument to def will be fully evaluated,
and this means calling bar passing it the first command-line
argument supplied by the user when running your program,
taking whatever bar returned and appending
a string literal to it.
The resulting string is then saved, and will be passed to def
when it will run.

In
defer def("defered line")()
def("defered line") and () are deferred call's arguments evaluated immediately.
def("defered line") evaluates to func(){ fmt.Println("clean up") } with side effects.

Related

Why does go panic recover to return value with local variable not work?

This panic recover code works with named return values.
func main() {
result, err := foo()
fmt.Println("result:", result)
if err != nil {
fmt.Println("err:", err)
}
}
func foo() (result int, err error) {
defer func() {
if e := recover(); e != nil {
result = -1
err = errors.New(e.(string))
}
}()
bar()
result = 100
err = nil
return
}
func bar() {
panic("panic happened")
}
Output
result: -1
err: panic happened
But why this code with local variables does not work?
func main() {
result, err := foo()
fmt.Println("result:", result)
if err != nil {
fmt.Println("err:", err)
}
}
func foo() (int, error) {
var result int
var err error
defer func() {
if e := recover(); e != nil {
result = -1
err = errors.New(e.(string))
}
}()
bar()
result = 100
err = nil
return result, err
}
func bar() {
panic("panic happened")
}
Output
result: 0
Any explanation to help me understanding the reason / basic concept of it? In the go tour basics the explanation is as followed.
Named return values
Go's return values may be named. If so, they are treated as variables defined at the top of the function.
So it should be the same, right?
Note that this has nothing to do with panic/recover, it is a feature of the defer statement.
... if the deferred function is a function literal and the surrounding
function has named result parameters that are in scope within the
literal, the deferred function may access and modify the result
parameters before they are returned. If the deferred function has
any return values, they are discarded when the function completes.
Spec: Return statements details this:
There are three ways to return values from a function with a result type:
The return value or values may be explicitly listed in the "return" statement. Each expression must be single-valued and assignable to the corresponding element of the function's result type.
The expression list in the "return" statement may be a single call to a multi-valued function. The effect is as if each value returned from that function were assigned to a temporary variable with the type of the respective value, followed by a "return" statement listing these variables, at which point the rules of the previous case apply.
The expression list may be empty if the function's result type specifies names for its result parameters. The result parameters act as ordinary local variables and the function may assign values to them as necessary. The "return" statement returns the values of these variables.
So basically if you use a return statement that explicitly lists the return values, those will be used, regardless if the result parameters are named or not.
If the result parameters are named, they act as ordinary local variables: you can read and write them. If the result parameters are named, you may use a "naked" return statement, without listing the values to return. If you do so, then the actual return values will be the values of the (named) result parameters. The same thing applies if your function does not reach a return statement due to panicing and recovering: once the deferred functions run, the actual return values will be the values of the named result parameters (which the deferred functions can change and "have a say" in what to return).
If you don't use named result parameters but you declare local variables, they are not special in this way: when the function returns, those are not used "automatically" as the result values (like they would be if they would be named result parameters and not local variables). So if you change them in a deferred function, that will not have any effect on the actual values returned. In fact, if you don't use named result parameters and your function panics and recovers, you can't specify the return values, they will be the zero values of the result types. That's why you see result: 0 (0 is the zero value for int) and no error (because error is an interface type and zero value for interface types is nil and you don't print the error if it's nil).
See related: How to return a value in a Go function that panics?
Might be a brief summary for #icza's anwser:
Named return variables use their final values for returning when the function teminate with no panic(return normally or recover from panic), so you can change them in defer recover func(), and the final values changed, so be the return values.
If use local variables, compiler can not know these local variables will be used as return variables until a normal return. Local variables might be changed in panic recover, but
the return statement has not been executed yet because the panic, so the local variables you defined was not treated as return variables, the return values will be the zero values of the return types.

What is the result of a single variable expression evaluation

Running the following snippet of Go code one can realize that function foo receives the value of the first argument actually set while evaluating the second argument of the function. This behavior might look counterintuitive so that we need to prove this to be a part of language spec, not something implementation-specific.
package main
import (
"fmt"
)
func setVal(s *int, v int) int {
old := *s
*s = v
return old
}
func foo(s int, p int) {
fmt.Printf("s = %d, p = %d\n", s, p)
}
func main() {
var s int
foo(s, setVal(&s, 99))
}
Programm outputs s = 99, p = 0, which means a modified value of variable s has been passed to the function.
Here is what the Go spec says regarding the case.
In a function call, ...arguments must be single-valued expressions ... arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function... Where usual order is the lexical left-to-right order.
A variable is a storage location for holding a value. ...A variable's value is retrieved by referring to the variable in an expression; it is the most recent value assigned to the variable.
Therefore foo(s, setVal(&s, 99)) is a function call, variable s and function setVal() are the single-valued expressions, and s is evaluated first. The last spec statement makes one assume the result of a variable evaluation is its value, so if that is true, function foo should receive initial value of the variable s.
But it in fact it appears that the function receives the value of the first argument been set at the moment of evaluating the second argument, which is a bit confusing.
Does that mean the evaluation order is broken or the result of a variable evaluation is not its value?
What you "miss" from the spec is Spec: Calls:
In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution.
Evaluating the parameters does not mean their values are read or "taken". The first parameter is s, its evaluation is s itself, but its value is not yet read. The second parameter is evaluated, which means setVal() is called and will modify the value of s.
Now that we have evaluated the parameters, their values are read, so s will have the value 99.
Evaluating s in the example is trivial, but of course that could be a more complex expression just like the second argument. Here's a more complex example:
s, s2 := new(int), new(int)
getFunc := func() func(s int, p int) { return foo }
first := func(a, b *int) *int { return a }
getFunc()(*first(s, s2), setVal(s, 99))
Call of the last function involves the following steps:
function value is evaluated: getFunc() is called, it's return value will be the function value
parameters are evaluated:
(a) first() is called, its return value is dereferenced;
(b) setVal() is called, its return value will be used
And now the values are taken: value of *s and the old value of s (value returned by setVal()).
This will output the same as your example, try it on the Go Playground.

Why can a normal return hide a panic that a named return correctly provides to the caller? [duplicate]

This question already has answers here:
How to return a value in a Go function that panics?
(3 answers)
Closed 3 years ago.
package main
import (
"fmt"
"log"
)
func catch(err *error) {
if r := recover(); r != nil {
*err = fmt.Errorf("recovered panic: %v", r)
}
}
func panicIf42(n int) {
if n == 42 {
panic("42!")
}
}
func NormalReturns(n int) error {
var err error
defer catch(&err)
panicIf42(n)
return err
}
func NamedReturns(n int) (err error) {
defer catch(&err)
panicIf42(n)
return
}
func main() {
err := NamedReturns(42)
log.Printf("NamedReturns error: %v", err)
err = NormalReturns(42)
log.Printf("NormalReturns error: %v", err)
}
output:
2009/11/10 23:00:00 NamedReturns error: recovered panic: 42!
2009/11/10 23:00:00 NormalReturns error: <nil>
Playground link
NormalReturns returns a nil error, but I would expect both NamedReturns and NormalReturns to return a non-nil error.
I thought named returns was just a code readability feature that declares and initializes returns for you, but it seems there's more to it. What am I missing?
I thought named returns was just a code readability feature that declares and initializes returns for you, but it seems there's more to it. What am I missing?
If you name the result parameters, their actual value at the time of returning to the caller will determine the returned values. Meaning you can change their values like other local variables, and if the expression list of the return statement is empty, their last assigned values will be used. Also if there are deferred functions, they can modify the values of the named result parameters after the return statement and before the function returns to its caller, and those modifications will be preserved. It also allows to modify return values in case of a panic, see How to return a value in a Go function that panics?
Spec: Return statements:
Regardless of how they [the return values] are declared, all the result values are initialized to the zero values for their type upon entry to the function. A "return" statement that specifies results sets the result parameters before any deferred functions are executed.
And Spec: Defer statements:
For instance, if the deferred function is a function literal and the surrounding function has named result parameters that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned.
In NormalReturns(): The return value is initialized to its zero value (which is nil for all interface types, including the builtin error type), and since the return statement is not reached (due to a panic in panicIf42()), it will stay nil. It doesn't matter if the local variable err is changed, that is not the result variable. It's just an ordinary variable. It will have no effect on the value returned
In general, if a function does not have named result variables, and if this function does not reach a return statement (e.g. due to a panic), it cannot have return values other than (meaning different from) the zero values of the result types.
In NamedReturns() the deferred catch() will modify the named result variable err. The changes are "preserved": whatever the named result variables hold will be returned when the function ends (which happens after calling deferred functions, if there are any). So even though the return statement is not reached here either, the catch() function changes the err result variable, and whatever is assigned to it will be used as the value returned.
More on the topic:
Go blog: Defer, Panic and Recover:
Deferred functions may read and assign to the returning function's named return values.
And also in Effective Go: Recover:
If doParse panics, the recovery block will set the return value to nil—deferred functions can modify named return values.

Why I get 0 and 1 in the following golang code example with defer

Call to defer produces different results for variables declared in two different ways
package main
import (
"fmt"
)
func c(i int) int {
defer func() { i++ }()
return i
}
func c1() (i int) {
defer func() { i++ }()
return i
}
func c2() (i int) {
defer func() { i++ }()
return 2
}
func main() {
fmt.Println(c(0)) // Prints 0
fmt.Println(c1()) // Prints 1
fmt.Println(c2()) // Prints 3 Thank you icza
}
https://play.golang.org/p/gfnnCZ--DkH
In the first example i is an (incoming) parameter. At the return statement the return value is evaluated, and the deferred function runs after this, and incrementing i in that has no effect on the return value.
In the second example i is the name of the result parameter. At the return statement you explicitly return the value i, which is then assigned to the return value i (this is a no-op). But deferred functions are allowed to modify the values of the return "variables", and if they do so, that will have an effect on the actual returned values.
This becomes clearer if we add another example:
func c2() (i int) {
defer func() { i++ }()
return 2
}
This function will return 3, because the return 2 statement will assign 2 to i, then the deferred function will increment this, and so the return value will be 3. Try this one on the Go Playground. Relevant part from the Spec: Return statements:
A "return" statement that specifies results sets the result parameters before any deferred functions are executed.
In general, if a function (or method) has named result parameters, the return values will always be the values of those variables, but must not forget that a return statement may assign new values to these result paramteters, and they may be modified by deferred functions after a return statement.
This is mentioned in the Spec: Defer statements:
For instance, if the deferred function is a function literal and the surrounding function has named result parameters that are in scope within the literal, the deferred function may access and modify the result parameters before they are returned.
It is also mentioned in the blog post Defer, Panic and Recover:
Deferred functions may read and assign to the returning function's named return values.
And also in Effective Go: Recover:
If doParse panics, the recovery block will set the return value to nil—deferred functions can modify named return values.
See related question: How to return a value in a Go function that panics?

Correct way to set Exit Code of Process?

In Go what is the proper way to set the exit code of the process?
I tried changing main func to
func main() int {
return -1
}
But this causes error func main must have no arguments and no return values
OK so there is os.Exit(code int), however this immediately aborts the process and does not exit cleanly (no deferreds are run for example).
I also found that panic will exit process and set status code to nonzero, this may be the best way, although it dumps a stack trace to console.
What is the right way to set the exit code?
Make os.Exit the last deferred function executed. Deferred functions are executed immediately before the surrounding function returns, in the reverse order they were deferred. For example,
package main
import (
"fmt"
"os"
)
func main() {
code := 0
defer func() {
os.Exit(code)
}()
defer func() {
fmt.Println("Another deferred func")
}()
fmt.Println("Hello, 世界")
code = 1
}
Output:
Hello, 世界
Another deferred func
[process exited with non-zero status]
Go Playground:
http://play.golang.org/p/o0LfisANwb
The Go Programming Language Specification
Defer statements
A "defer" statement invokes a function whose execution is deferred to
the moment the surrounding function returns, either because the
surrounding function executed a return statement, reached the end of
its function body, or because the corresponding goroutine is
panicking.
DeferStmt = "defer" Expression .
The expression must be a function or method call; it cannot be
parenthesized. Calls of built-in functions are restricted as for
expression statements.
Each time the "defer" statement executes, the function value and
parameters to the call are evaluated as usual and saved anew but the
actual function body is not executed. Instead, deferred functions are
executed immediately before the surrounding function returns, in the
reverse order they were deferred.

Resources