One easy way to understand is injecting debug messages before and after lines. Consider the following modified codes:
package main
import (
"fmt"
)
func liner(functionName string, format string, a ...interface{}) {
info := fmt.Sprintf(format, a...)
fmt.Printf("[%s] %v\n", functionName, info)
}
func main() {
liner("main", "debug line before define x")
x := 2
liner("main", "debug line before calling foo, x=%v", x)
foo(x)
liner("main", "debug line after foo, x=%v", x)
fmt.Println(x)
liner("main", "debug line after foo, after print, x=%v", x)
}
func foo(y int) {
liner("foo", "debug line enters foo. y=%v", y)
fmt.Println(y)
liner("foo", "debug line, after print y, y=%v", y)
y = 43
liner("foo", "debug line, after set y, y=%v", y)
fmt.Println(y)
liner("foo", "debug line, end foo, y=%v", y)
}
Output with mixed-up liner
:
[main] debug line before define x
[main] debug line before calling foo, x=2
[foo] debug line enters foo. y=2
2
[foo] debug line, after print y, y=2
[foo] debug line, after set y, y=43
43
[foo] debug line, end foo, y=43
[main] debug line after foo, x=2
2
[main] debug line after foo, after print, x=2
The main
function is indicated by [main]
tag while foo
function indicated by [foo]
tag.
Notice that at the first line of [foo]
tag after the cpu enters the function, y
is the same as x
instead of 0
. This is what it meant by your lecturer, the value was passed in from x
to y
.
Due to the nature of “Passed by Value”, when [main]
resumes back after [foo]
exited, x
is still back to 2
instead of 43
, like how you modified it inside foo
function. This essentially means that the function clones the value into y
instead of modifying x
directly.
NOTE:
You can only do this liner
experiment when not dealing with concurrency. Otherwise, it’s pointless
and incomprehensible. You need a different tool for that.