J.Doe
J.Doe

Reputation: 413

Go resetting closure variable

I came upon an example of closures in Go here: https://gobyexample.com/closures

It gives a pretty straight-forward example of closure scoping in Go. I changed how i is initialized from "i := 0" to "i := *new(int)".

func intSeq() func() int {
    i := *new(int)
    return func() int {
        i += 1
        return i
    }
}

func main() {

    // We call `intSeq`, assigning the result (a function)
    // to `nextInt`. This function value captures its
    // own `i` value, which will be updated each time
    // we call `nextInt`.
    nextInt := intSeq()

    // See the effect of the closure by calling `nextInt`
    // a few times.
    fmt.Println(nextInt())
    fmt.Println(nextInt())
    fmt.Println(nextInt())

    // To confirm that the state is unique to that
    // particular function, create and test a new one.
    newInts := intSeq()
    fmt.Println(newInts())
}

The output of this is still 1, 2, 3, 1. Does the variable 'i' in intSeq() not get reallocated everytime nextInt() in main() is called?

Upvotes: 0

Views: 1261

Answers (3)

eugenioy
eugenioy

Reputation: 12383

Take a look at how you implemented intSeq.

func intSeq() func() int {
    i := *new(int)
    return func() int {
        i += 1
        return i
    }
}

The initialization of i is outside of the function it returns.

So the only time a new pointer is allocated is when you actually call intSeq.

Since you are doing that just two times, that's how many different pointers you got.

That explains why the value is not reset when you just call nextInt (note that executing nextInt means just executing the function returned, which looks like:

func() int {
   i += 1
   return i
}

That would not reset the value of i but rather keep incrementing it (until you create a new one by calling intSeq again).

I hope that clarifies.

Upvotes: 3

Corvus Crypto
Corvus Crypto

Reputation: 2291

No it doesn't. That's the point of the closure. You are initializing an integer variable and storing it on the heap for use by the function the intSeq() function returns. There is no variable initialization happening in the nextInt() function

You will get a new function that uses a new sequence counter starting at 0 for each call to intSeq()

Edit: to add to this this is a bad way to get the current behavior. A better way would be to create a new sequence type that contains the method nextInt() int. E.g.:

type Sequence struct {
    counter int
}

func (s *Sequence) nextInt() int {
    s.counter++
    return s.counter
}

func main() {
    intSeq := new(Sequence)
    fmt.Println(intSeq.nextInt())
    fmt.Println(intSeq.nextInt())
    fmt.Println(intSeq.nextInt())
}

Upvotes: 2

Adrian
Adrian

Reputation: 46423

There is no point in doing i := *new(int). That line says:

  1. Allocate a new int
  2. Create a pointer to it
  3. Dereference the pointer
  4. Assign the value to i

This is no different from i := 0 or var int i, but there's the extra step in the middle of creating, dereferencing, and discarding the pointer that never gets used.

If you want a pointer to an int, use i := new(int). *new anywhere is a pointless invocation and a code smell.

Upvotes: 0

Related Questions