Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Embedded struct

Tags:

go

Is it possible to inherit methods of a type without using embedded structs?

The first snippet of code is working code that embeds the Property struct in Node and I'm able to call node.GetString that's a method on Properties. The thing I don't like about this is when I initialize Node I have(?) to initialize the Properties struct within it. Is there a way around this?

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 struct {
    *Properties
}

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

Ultimately, I would like to do something like the following. Where Node is of type Properties and initializing does not require initializing a Property struct too. The following code doesn't work but may be clear what my goal is.

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(singleNode.GetString("test")) // :D
}

I'll be adding more structs that will use Properties's methods which is why I'm asking. If I only had Node, I would just have methods for Node and be done. But because I'll have more than Node I find it kind of redundant to add the same methods to all the structs that embed Properties

I guess more to the exact problem, I want to use Properties methods from Node without having to initialize Properties.

like image 437
Jeff Avatar asked Dec 04 '15 01:12

Jeff


People also ask

What is struct embedding?

Go by Example: Struct EmbeddingGo supports embedding of structs and interfaces to express a more seamless composition of types. This is not to be confused with //go:embed which is a go directive introduced in Go version 1.16+ to embed files and folders into the application binary.

What is type embedding?

From the article structs in Go, we know that a struct type can have many fields. Each field is composed of one field name and one field type. In fact, sometimes, a struct field can be composed of a field type only. The way to declare struct fields is called type embedding.

What is embedded interface in Golang?

In Go language, the interface is a collection of method signatures and it is also a type means you can create a variable of an interface type. As we know that the Go language does not support inheritance, but the Go interface fully supports embedding.

Are structs interfaces Golang?

Structs and interfaces are Go's way of organizing methods and data handling. Where structs define the fields of an object, like a Person's first and last name. The interfaces define the methods; e.g. formatting and returning a Person's full name.


2 Answers

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!

like image 193
Endophage Avatar answered Oct 17 '22 11:10

Endophage


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 .

like image 24
Salah Eddine Taouririt Avatar answered Oct 17 '22 10:10

Salah Eddine Taouririt