In Go, all parameters passed to a function are done so by value. This means a local copy of the passed values is created inside the called function. There is no inherent concept of passing parameter values by reference. The following code illustrates this mechanism by modifying the value of the passed parameter, val
, inside the dbl
function:
package main import ( "fmt" "math" ) func dbl(val float64) { val = 2 * val // update param fmt.Printf("dbl()=%.5f ", val) } func main() { p := math.Pi fmt.Printf("before dbl() p = %.5f ", p) dbl(p) fmt.Printf("after dbl() p = %.5f ", p) }
golang.fyi/ch05/funcpassbyval.go
When the program runs, it produces the following output that chronicles the state of the p
variable before it is passed to the dbl
function. The update is made locally to the passed parameter variable inside the dbl
function, and lastly the value of the p
variable after the dbl
function is called:
$> go run funcpassbyval.go before dbl() p = 3.14159 dbl()=6.28319 after dbl() p = 3.14159
The preceding output shows that the original value assigned to variable p
remains variable unchanged, even after it is passed to a function that seems to update its value internally. This is because the val
parameter in the dbl
function receives a local copy of the passed parameter.
While the pass-by-value is appropriate in many cases, it is important to note that Go can achieve pass-by-reference semantics using pointer parameter values. This allows a called function to reach outside of its lexical scope and change the value stored at the location referenced by the pointer parameter as is done in the half
function in the following example:
package main import "fmt" func half(val *float64) { fmt.Printf("call half(%f) ", *val) *val = *val / 2 } func main() { num := 2.807770 fmt.Printf("num=%f ", num) half(&num) fmt.Printf("half(num)=%f ", num) }
golang.fyi/ch05/funcpassbyref.go
In the previous example, the call to the half(&num)
function in main()
updates, in place, the original value referenced by its num
parameter. So, when the code is executed, it shows the original value of num
and its value after the call to the half
function:
$> go run funcpassbyref.go num=2.807770 call half(2.807770) half(num)=1.403885
As was stated earlier, Go function parameters are passed by value. This is true even when the function takes a pointer value as its parameter. Go still creates and passes in a local copy of the pointer value. In the previous example, the half
function receives a copy of the pointer value it receives via the val
parameter. The code uses pointer operator (*
) to dereference and manipulate, in place, the value referenced by val
. When the half
function exits and goes out of scope, its changes are accessible by calling the main
function.
Functions can be written as literals without a named identifier. These are known as anonymous functions and can be assigned to a variable to be invoked later as shown in the following example:
package main import "fmt" var ( mul = func(op0, op1 int) int { return op0 * op1 } sqr = func(val int) int { return mul(val, val) } ) func main() { fmt.Printf("mul(25,7) = %d ", mul(25, 7)) fmt.Printf("sqr(13) = %d ", sqr(13)) }
golang.fyi/ch05/funcs.go
The previous program shows two anonymous functions declared and bound to the mul
and sqr
variables. In both cases, the functions take in parameters and return a value. Later in main()
, the variables are used to invoke the function code bound to them.
It is worth noting that an anonymous function does not have to be bound to an identifier. The function literal can be evaluated, in place, as an expression that returns the function's result. This is done by ending the function literal with a list of argument values, enclosed in parentheses, as shown in the following program:
package main import "fmt" func main() { fmt.Printf( "94 (°F) = %.2f (°C) ", func(f float64) float64 { return (f - 32.0) * (5.0 / 9.0) }(94), ) }
golang.fyi/ch05/funcs.go
The literal format not only defines the anonymous function, but also invokes it. For instance, in the following snippet (from the previous program), the anonymous function literal is nested as a parameter to fmt.Printf()
. The function itself is defined to accept a parameter and returns a value of type float64
.
fmt.Printf( "94 (°F) = %.2f (°C) ", func(f float64) float64 { return (f - 32.0) * (5.0 / 9.0) }(94), )
Since the function literal ends with a parameter list enclosed within parentheses, the function is invoked as an expression.
Go function literals are closures. This means they have lexical visibility to non-local variables declared outside of their enclosing code block. The following example illustrates this fact:
package main import ( "fmt" "math" ) func main() { for i := 0.0; i < 360.0; i += 45.0 { rad := func() float64 { return i * math.Pi / 180 }() fmt.Printf("%.2f Deg = %.2f Rad ", i, rad) } }
github.com/vladimirvivien/learning-go/ch05/funcs.go
In the previous program, the function literal code block, func() float64 {return deg * math.Pi / 180}()
, is defined as an expression that converts degrees to radians. With each iteration of the loop, a closure is formed between the enclosed function literal and the outer non-local variable, i
. This provides a simpler idiom where the function naturally accesses non-local values without resorting to other means such as pointers.
3.144.39.144