Passing parameters to function closure

百般思念 提交于 2019-12-21 03:30:42

问题


I'm trying to understand the difference in Go between creating an anonymous function which takes a parameter, versus having that function act as a closure. Here is an example of the difference.

With parameter:

func main() {
  done := make(chan bool, 1)
  go func(c chan bool) {
    time.Sleep(50 * time.Millisecond)
    c <- true
  }(done)
  <-done
}

As closure:

func main() {
  done := make(chan bool, 1)
  go func() {
    time.Sleep(50 * time.Millisecond)
    done <- true
  }()
  <-done
}

My question is, when is the first form better than the second? Would you ever use a parameter for this kind of thing? The only time I can see the first form being useful is when returning a func(x, y) from another function.


回答1:


The difference between using a closure vs using a function parameter has to do with sharing the same variable vs getting a copy of the value. Consider these two examples below.

In the Closure all function calls will use the value stored in i. This value will most likely already reach 3 before any of the goroutines has had time to print it's value.

In the Parameter example each function call will get passed a copy of the value of i when the call was made, thus giving us the result we more likely wanted:

Closure:

for i := 0; i < 3; i++ {
    go func() {
        fmt.Println(i)
    }()
}

Result:

3
3
3

Parameter:

for i := 0; i < 3; i++ {
    go func(v int) {
        fmt.Println(v)
    }(i)
}

Result:

0
1
2

Playground: http://play.golang.org/p/T5rHrIKrQv




回答2:


When to use parameters

Definitely the first form is preferred if you plan to change the value of the variable which you don't want to observe in the function.

This is the typical case when the anonymous function is inside a for loop and you intend to use the loop's variables, for example:

for i := 0; i < 10; i++ {
    go func(i int) {
        fmt.Println(i)
    }(i)
}

Without passing the variable i you might observe printing 10 ten times. With passing i, you will observe numbers printed from 0 to 9.

When not to use parameters

If you don't want to change the value of the variable, it is cheaper not to pass it and thus not create another copy of it. This is especially true for large structs. Although if you later alter the code and modify the variable, you may easily forget to check its effect on the closure and get unexpected results.

Also there might be cases when you do want to observe changes made to "outer" variables, such as:

func GetRes(name string) (Res, error) {
    res, err := somepack.OpenRes(name)
    if err != nil {
        return nil, err
    }

    closeres := true
    defer func() {
        if closeres {
            res.Close()
        }
    }()

    // Do other stuff
    if err = otherStuff(); err != nil {
        return nil, err // res will be closed
    }

    // Everything went well, return res, but
    // res must not be closed, it will be the responsibility of the caller
    closeres = false

    return res, nil // res will not be closed
}

In this case the GetRes() is to open some resource. But before returning it other things have to be done which might also fail. If those fail, res must be closed and not returned. If everything goes well, res must not be closed and returned.



来源:https://stackoverflow.com/questions/30183669/passing-parameters-to-function-closure

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!