0

could someone be so kind to explain this behaviour to me? I can't wrap my mind around why this happens (still learning Go). My concrete questions are marked in the source code with QUESTION.

Thanks, Michael

package main

// Define a simple type and functions on this type.
type Foo struct{}
var foo *Foo

func (f *Foo) function() {
    if f == nil {
        panic("Why is f nil?")
    }
}

// Create a wrapper struct containg method pointers to a global receiver variable.
type Example struct {
    f func()
}

var bar = &Example{
    // QUESTION: When is foo actually evaluated? It seems at definition of bar and then it's fixed? 
    // QUESTION: And why is its value at runtime not used to determine the (now initialized) receiver target?
    f: foo.function,
}

// Initialize the global variable.
func init() {
    foo = new(Foo)
}

// Call the function on foo, which should be initialized in init.
func main() {
    bar.f()
}
mlesniak
  • 11
  • 4

1 Answers1

3

This is the relevant section in the language spec:

https://golang.org/ref/spec#Package_initialization

All global variables are initialized based on dependency analysis. So when this initializes:

var bar = &Example{
    f: foo.function
}

it requires foo to be initilized. Since there are no initializers defined for foo, it is nil. Once all variable initialization is complete, init() function runs and sets foo to non-nil value.

If you change the declaration to:

var foo = new(Foo)

it works.

Burak Serdar
  • 46,455
  • 3
  • 40
  • 59
  • Thanks Burkar. Before main is called, foo received a new value. I would expect that in the main call (at runtime) foo.function would resolve to the new (non-nil) value of foo. Or, question reformulated: how are function pointers with method receivers bound at runtime to their respective receivers? – mlesniak Jan 29 '20 at 16:35
  • ...to clarify my question: why is foo nil and not initialized with the value from `init()` inside the function `func (f *Foo) function`? – mlesniak Jan 29 '20 at 16:39
  • In your code, `foo` is first initialized to nil, then `f` gets `foo.function` with nil receiver (because `foo` is nil), and then `foo` is set to a non-nil value. The function call, when you assign it, is assigned along with its receiver. At the time it is assigned, the receiver was nil, so that's what's called. – Burak Serdar Jan 29 '20 at 16:41
  • Thanks, I wasn‘t aware that function call references are assigned in combination with their receiver but assumed that the receiver is dynamically resolved. This explains the behavior. – mlesniak Jan 29 '20 at 17:12
  • To be precise: method calls are assigned with their receivers. What amazed me when I started Go was that you can pass a method where a regular function was expected, and it would be called with the correct receiver. – Burak Serdar Jan 29 '20 at 17:13
  • For future readers please also read https://stackoverflow.com/questions/28251283/golang-function-alias-on-method-receiver – mlesniak Jan 29 '20 at 17:14