简体   繁体   中英

Uninitialized Embedded Struct

I'm a bit perplexed by this go code. I have a struct (Outer) with an embedded struct (Inner), but when I initialize Outer, I intentionally leave the embedded struct uninitialized.

type Inner struct {
    value int
}
func (i *Inner) MyFunc() string {
    return "inner"
}
func (i *Inner) OnlyInner() string {
    return "only inner stuff"
}
type Outer struct {
    *Inner
}
func (o *Outer) MyFunc() string {
    return "outer"
}
func main() {
    // embedded struct is *not* initialized
    o := &Outer{}
    fmt.Println(o.Inner)
    fmt.Println(o.Inner.MyFunc())
    fmt.Println(o.Inner.OnlyInner())
    //fmt.Println(o.Inner.value)
}

Output:

<nil>
inner
only inner stuff

And if I uncomment the last line (with o.Inner.value ), I get a nil pointer dereference error.

What's up here? The effective go page says ( https://golang.org/doc/effective_go.html#embedding ):

When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one.

It seems like in my case, the inner type is <nil> , yet the method calls execute without problem. What's going on under the hood?

A method can be called with a nil receiver, as long as you do not dereference the receiver itself.

This means that the following works playground :

package main

import (
    "fmt"
)

type foo struct {
    val int
}

func (f *foo) Print() {
    fmt.Println("Receiver:", f)
}

func (f *foo) PrintVal() {
    fmt.Println("Val: ", f.val)
}

func main() {
    var f *foo
    f.Print()
    //f.PrintVal()
}

f.Print() works without issues since we're just printing a pointer, we're not trying to dereference it.

However, f.PrintVal attempts to dereference a nil pointer, causing a panic.

When in doubt, remember that the methods in this example are equivalent to functions that take the receiver as first parameter:

func Print(f *foo)
func PrintVal(f *foo)

This is mentioned in the spec under method declarations :

The type of a method is the type of a function with the receiver as first argument. For instance, the method Scale has type

func(p *Point, factor float64)

However, a function declared this way is not a method.

This makes it clear that the receiver is nothing special, it can be nil as long as you don't dereference it.

The methods of the uninitialized struct are being called with a nil-receiver. If in the methods used that receiver you would get a panic. It is valid to call a method using a nil receiver, and the method could modify its behavior by checking if the receiver is nil.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM