Embedded struct

So you're running into an idiosyncrasy of Go here. Embedding is the only way in which methods of one struct can get "promoted" to appear to exist on another struct. While it feels intuitive that type Node Properties should expose the Properties methods on Node, that effect of that syntax is for Node to take on the memory layout of Properties but not any of its methods.

It doesn't explain why this design choice was made but the Go Spec is at least specific if dry. If you read it exactly as it appears, with no interpretation, it is very accurate:

The method set of an interface type is its interface. The method set of any other type T consists of all methods declared with receiver type T

GetString has a receiver of type Properties not Node, seriously, interpret the spec like you're an accountant with no imagination. With that said:

Further rules apply to structs containing anonymous fields, as described in the section on struct types.

...

A field or method f of an anonymous field in a struct x is called promoted if x.f is a legal selector that denotes that field or method f.

Promoted fields act like ordinary fields of a struct except that they cannot be used as field names in composite literals of the struct.

Given a struct type S and a type named T, promoted methods are included in the method set of the struct as follows:

  • If S contains an anonymous field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.
  • If S contains an anonymous field *T, the method sets of S and *S both include promoted methods with receiver T or *T.

That line about composite literals is this thing that forces you to declare Properties inside every Node you create.

p.s. Hi Jeff!


The short answer to your last question is simply No.

There is a big difference between type declaration and embedding in golang, you can make your last example working by manually make a type conversion between Node and Properties:

package main

import "fmt"

type Properties map[string]interface{}

func (p Properties) GetString(key string) string {
    return p[key].(string)
}

type Nodes map[string]*Node

type Node Properties

func main() {
    allNodes := Nodes{"1": &Node{"test": "foo"}} // :)
    singleNode := allNodes["1"]
    fmt.Println(Properties(*singleNode).GetString("test")) // :D
}

But it's clearly that is not what you want, you want a struct embedding with a syntax of type aliasing, which is not possible in golang, I think that you should stuck with the your first approach and ignore the the fact the code is redundant and ugly .

Tags:

Go