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
.
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.
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.
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.
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.
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 .
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With