When a function is given as an argument to a function, is it pass-by-value or pass-by-pointer

huangapple go评论58阅读模式
英文:

When a function is given as an argument to a function, is it pass-by-value or pass-by-pointer

问题

这是一个例子:

func funcB() { fmt.Println("Hi") })
funcA(funcB)

funcA接收到的是指向funcB的指针吗?还是接收到了一个完整的不可变函数?

所以,如果我执行以下代码:

func funcA() { fmt.Println("Hi") })
funcB(funcA)
funcC(funcA)
funcD(funcA)
funcE(funcA)
funcF(funcA)

所有的函数调用都会创建一个新的funcA对象吗?还是它们都接收到了同一个指向funcA的指针?

英文:

Take this example -

func funcB() { fmt.Println("Hi") })
funcA(funcB)

Is funcA receiving a pointer to funcB? Or is it receiving a full-blown immutable function?

So, if I execute this code -

func funcA() { fmt.Println("Hi") })
funcB(funcA)
funcC(funcA)
funcD(funcA)
funcE(funcA)
funcF(funcA)

are all of the function calls creating a new funcA object, or are all of them receiving the same pointer to funcA?

答案1

得分: 3

函数没有状态可以通过值或引用传递,也就是说,它们只是可执行的代码。当你将一个函数传递给另一个函数时,你实际上是给出了函数体可执行代码的某个引用。

匿名函数呢?

你也可以将匿名函数或闭包存储到func()值中。在这种情况下,你的问题变得更有趣,因为闭包可以保存状态。

闭包是通过值还是引用传递给函数的?

func call(f func()) {
    f()
}

func main() {
    var i int

    f := func() {
        fmt.Println(i)
        i++
    }

    call(f)
    call(f)
}

运行上面的代码会输出:

0
1

我们可以得出结论,闭包的状态(即捕获的变量i)在call()的多次调用之间是共享的。原则上,可以通过以下方式实现这种行为:

  1. 闭包通过引用捕获其环境,并通过值或引用传递。
  2. 闭包通过值捕获其环境,并仅通过引用传递。

为了找出闭包如何捕获其环境,我们可以在main()中更改i的值,并查看闭包所看到的i值是否受到影响。如果我们将以下代码附加到main()

i = 100
call(f)

那么将显示100。因此,imain()和闭包之间是共享的(即闭包没有自己的i副本)。因此,闭包通过引用捕获其环境(我们现在可以排除上述第2个选项)。

到目前为止,我们还没有找到闭包是通过值还是通过引用传递的。然而,我们已经知道闭包捕获的状态实际上是通过引用传递给函数的,因为闭包持有对捕获状态的引用。这很可能是最关键的洞察力。

即便如此,我们可以看到闭包是通过值传递的:

func change(f func()) {
    f = func() {
        fmt.Println("replaced")
    }
}

func main() {
    f := func() {
        fmt.Println("original")
    }

    change(f)
    f() // 输出 "original"
}

结论

  • 函数只是代码。它们不存储状态。函数是某些可执行代码的内存地址。当你将一个函数传递给另一个函数时,你实际上是给出了这个地址。

  • 匿名函数或闭包可以保存与封闭环境对应的状态。尽管闭包是通过值传递的,但它们所持有的状态是对捕获环境的引用。因此,闭包捕获的环境实际上是通过引用传递的。

英文:

Functions hold no state to be passed either by value or by reference, i.e., they are just executable code. When you pass a function to another function, you are giving some reference to the executable code of the function's body.

How about anonymous functions?

You can also store an anonymous function or closure into a func() value. In this case, your question becomes more interesting because closures can hold state.

Is a closure passed to a function provided by value or by reference?

func call(f func()) {
	f()
}

func main() {
	var i int

	f := func() {
		fmt.Println(i)
        i++
	}

	call(f)
	call(f)
}

Running the code above outputs:

0
1

We can conclude that the closure's state – i.e., the captured i variable – is somehow being shared between calls to call(). In principle, this behavior could have been achieved by:

  1. The closure captured its environment by reference and was passed either by value or by reference.
  2. The closure captured its environment by value and was passed by reference only.

To find out how the closure captures its environment, we can change the value of i in main() and see whether the i value that the closure sees gets affected. If we append the code below to main():

i = 100
call(f)

Then, 100 is displayed. Therefore, i is shared between main() and the closure (i.e., the closure doesn't have its own copy of i). So, closures capture their environment by reference (we can now rule out the 2. option above).

At this point, we still haven't found whether a closure is passed by value or by reference. However, we already know that a closure's captured state is, in effect, passed by reference to functions because a closure holds references to the captured state. This is very likely the most critical insight.

Even so, we can see closures are passed by value:

func change(f func()) {
	f = func() {
		fmt.Println("replaced")
	}
}

func main() {
	f := func() {
		fmt.Println("original")
	}

	change(f)
	f() // prints "original"
}

Conclusion

  • Functions are just code. They don't store state. A function is a memory address to some executable code. When you pass a function to another function, you are giving this address.

  • Anonymous functions or closures can hold state that corresponds to the enclosing environment. Even though closures are passed by value, the states they hold are references to the environment they captured. So, the net effect is that the closure's captured environment is passed by reference.

huangapple
  • 本文由 发表于 2022年8月6日 19:45:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/73259450.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定