Composition vs inheritance with anonymous struct - go

I was reading this slideshow, which says:
var hits struct {
sync.Mutex
n int
}
hits.Lock()
hits.n++
hits.Unlock()
How does that work exactly? Seems like hits isn't composed of a mutex and integer, but is a mutex and integer?

It is composition. Using an anonymous field (embedded field), the containing struct will have a value of the embedded type, and you can refer to it: the unqualified type name acts as the field name.
So you could just as easily write:
hits.Mutex.Lock()
hits.n++
hits.Mutex.Unlock()
When you embed a type, fields and methods of the embedded type get promoted, so you can refer to them without specifying the field name (which is the embedded type name), but this is just syntactic sugar. Quoting from Spec: Selectors:
A selector f may denote a field or method f of a type T, or it may refer to a field or method f of a nested embedded field of T.
Beyond the field / method promotion, the method set of the embedder type will also contain the method set of the embedded type. Quoting from Spec: Struct types:
Given a struct type S and a defined type T, promoted methods are included in the method set of the struct as follows:
If S contains an embedded 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 embedded field *T, the method sets of S and *S both include promoted methods with receiver T or *T.
This is not inheritance in the OOP sense, but something similar. This comes handy when you want to implement an interface: if you embed a type that already implements the interface, so will your struct type. You can also provide your own implementation of some methods, which gives the feeling of method overriding, but must not be forgetten that selectors that denote methods of the embedded type will get the embedded value as the receiver (and not the embedder value), and selectors that denote your methods defined on the struct type (that may or may not "shadow" a method of the embedded type) will get the embedder struct value as the receiver.

It's called embedding, hits is composed of a sync.Mutex and an int. This should be true since there is really no inheritance in Go. This is more of a "has a" rather than an "is a" relationship between the members and the struct.
Read here for a more complete explanation
Quoted from the link
The methods of embedded types come along for free
That means you can access them like hits.Lock() instead of the longer form hits.Mutex.Lock() because the function Lock() is not ambiguous.

See the Go-syntax representation of hits variable:
fmt.Printf("%#v\n", &hits)
// &struct { sync.Mutex; n int }{Mutex:sync.Mutex{state:0, sema:0x0}, n:1}
When you declare the variable, it simply initializes the fields in struct with their default values.
Also, compiler automatically sets the name of the embedded struct as a field. So you can also access like:
hits.Mutex.Lock()
hits.Mutex.Unlock()
And you have access to all methods and exported fields (if any) of sync.Mutex.

Related

Why can't I assign a embedded struct to a parent struct in go?

I have below code try to assign embed struct to its parent struct. There are two set of structure: Guider is the parent struct, DataBlock extends from it. The method func Put(x Guider) accept a parameter with type Guider. It works when I pass a DataBlock variable.
However, the other case is Mock extends from zerolog.Event, but it fails to pass the parameter on the method Test(e zerolog.Event)
I got the following error:
cannot use m (variable of type Mock) as type zerolog.Event in argument to Test
Why are these two cases works differently? How can I make them both work?
package main
import (
"fmt"
"github.com/rs/zerolog"
)
type Guider interface {
Guid() string
}
type FSEntity struct {
guid string
}
func (e FSEntity) Guid() string {
return e.guid
}
func Put(x Guider) {
fmt.Printf("%+v\n", x)
}
type Mock struct {
zerolog.Event
}
func Test(e zerolog.Event) {
}
//Child struct:
type DataBlock struct {
FSEntity
data []byte
}
func main() {
myVar := DataBlock{}
myVar.guid = "test"
myVar.data = []byte("moar test")
Put(myVar) // it works
m := Mock{}
Test(m) // it doesn't work. cannot use m (variable of type Mock) as type zerolog.Event in argument to Test
}
First, a couple of definitions:
Polymorphism
Polymorphism is the provision of a single interface to entities of different types or the use of a single symbol to represent multiple different types.
Subtyping
Subtyping (also subtype polymorphism or inclusion polymorphism) is a form of type polymorphism in which a subtype is a datatype that is related to another datatype (the supertype) by some notion of substitutability, meaning that program elements, typically subroutines or functions, written to operate on elements of the supertype can also operate on elements of the subtype
Inheritance
In object-oriented programming, inheritance is the mechanism of basing an object or class upon another object (prototype-based inheritance) or class (class-based inheritance), retaining similar implementation.
Object composition
Object composition and object aggregation are closely related ways to combine objects or data types into more complex ones.
Golang follows composition over inheritance principle, e.g. it doesn't support inheritance. So when you're saying
Mock extends from zerolog.Event
you actually mean that Mock includes zerolog.Event struct.
The way Golang implements polymorphism is interface. All types that implement some interface can be used in its place. It's what you see when use Guider.
However, it doesn't work for simple structs. zerolog.Event is a struct inside Mock.
So, normally, Test function should accept some interface as a parameter, and both mock and real event should implement this interface. However, it looks like zerolog doesn't provide interface for Event. So instead you should access the Event field of you struct. Example
Put(myVar) is legal because myVar is a DataBlock which contains (not inherits from and not implements) an FSEntity which in turn implements the Guider interface.
Since Put accepts a Guider, the reference to myVar is compatible, by virtue of the anonymous FSEntity field it contains which implements Guider. The implementation of Guider on FSEntity is (in effect) elevated to the containing struct (providing a means of delegating interfaces). This only occurs if the contained field is anonymous.
But in the case of Test(m), the function accepts a zerolog.Event which is a struct type, not an interface. As such, there is no "delegation" possible. Test() must be passed a zerolog.Event and in this scenario, this requires that you use the type name of the anonymous field:
Type(m.Event)
Some bonus info:
If DataBlock contained two anonymous fields which both implemented Guider then implicit delegation/elevation cannot take place; golang does not know which of the contained implementations should be delegated to/elevated (if any). In that scenario you must again use the name of the field that you wish to pass to the Put() function:
// given...
type Foo string
func (f Foo) Guid() string {
return string(f)
}
// and...
type DataBlock struct {
FSEntity
Foo
data []byte
}
// then...
Put(myVar) // is now illegal
// and must instead use either/or:
Put(myVar.FSEntity)
Put(myVar.Foo)
Whether implicit or explicit, the crucial distinction is that it is a field of the DataBlock (myVar) that is passed to Put(), not myVar itself.
If you want to pass the DataBlock to Put(), using a Guider interface, then DataBlock must itself implement the Guider interface.
Take this with a grain of salt, since I'm not familiar with zerolog package.
Your Guider is an interface, which might have any underlying type as long as Guid() method is satisfied. I assume this is happening through DataBlock containing FSEntity, which itself implements Guid() method, therefore satisfies MIGHT the interface.
On the other hand, I don't know what methods should be implemented to satisfy zerolog.Event or if it's even an interface, or a struct straight up. If it's an interface, you might need to implement it's required methods to be able to use DataBlock as zerolog.Event type. You might want/need to dig into that direction for some very specific answers.

How to define type without operations allowed on the underlying type?

If I define a type definition
type X int64
Why is it that I can then do
var x X = 123
x = x + 1
The x behaves as if it was the underlying int64, which I don't want. Or that it allows operations on the underlying type to be performed on this new type?
One of the reasons I'd define a new type is to hide the underlying type and define my own operations on it.
Creating a new defined type will dissociate any methods on the underlying type, but it does not dissociate functionality with operators such as + - / *.
A defined type may have methods associated with it. It does not inherit any methods bound to the given type
You should base your type on an underlying type with the desirable operators. For example, if you don't want to have arithmetic operators, you can derive from a struct.
If your type still needs the arithmetic capabilities of an int64 for internal reasons, you can hide it as an un-exported field in the struct. For example:
type X struct {
number int64
}

How we call an struct inside of another struct as embedded?

Why we don't call person field as embedded?
“type user struct {
 name  string
 email string
}
 
type admin struct {
 person user  // NOT Embedding
 level  string
}”
But in other cases like below we call it embedded:
“type user struct {
 name  string
 email string
}
 
type admin struct {
 user  // Value Semantic Embedding
 level  string
}”
What I think is that person is also embedded like value/pointer semantic embedding. What I'm missing here?
Because that's how the Go language specification defines it:
A field declared with a type but no explicit field name is called an embedded field.
I can see how the term "embedded" would be confusing. After all, named and unnamed fields end up with the same memory layout, "embedded" into the parent struct. "Anonymous field" might have been a better name for it, but that's not the name that the Go language designers chose.
With the first code you can't treat an admin object as a user object, like using member access or type assertion. This also affects how an embedding struct satisfies interfaces.
For example, the following code works with proper embedding, but not a simple member struct:
var a admin
a.name = "asdfg"
u := a.(user)

Use map[string]SpecificType with method of map[string]SomeInterface into

I get cannot use map[string]MyType literal (type map[string]MyType) as type map[string]IterableWithID in argument to MapToList with the code below, how do I pass in a concrete map type to method that expects a interface type?
https://play.golang.org/p/G7VzMwrRRw
Go's interface convention doesn't quite work the same way as in, say, Java (and the designers apparently didn't like the idea of getters and setters very much :-/ ). So you've got two core problems:
A map[string]Foo is not the same as a map[string]Bar, even if Bar implements Foo, so you have to break it out a bit (use make() beforehand, then assign in a single assignment).
Interface methods are called by value with no pointers, so you really need to do foo = foo.Method(bar) in your callers or get really pointer-happy to implement something like this.
What you can do to more-or-less simulate what you want:
type IterableWithID interface {
SetID(id string) IterableWithID // use as foo = foo.SetID(bar)
}
func (t MyType) SetID(id string) IterableWithID {
t.ID = id
return t
}
...and to deal with the typing problem
t := make(map[string]IterableWithID)
t["foo"] = MyType{}
MapToList(t) // This is a map[string]IterableWithID, so compiler's happy.
...and finally...
value = value.SetID(key) // We set back the copy of the value we mutated
The final value= deals with the fact that the method gets a fresh copy of the value object, so the original would be untouched by your method (the change would simply vanish).
Updated code on the Go Playground
...but it's not particularly idiomatic Go--they really want you to just reference struct members rather than use Java-style mutators in interfaces (though TBH I'm not so keen on that little detail--mutators are supes handy to do validation).
You can't do what you want to do because the two map types are different. It doesn't matter that the element type of one is a type that implements the interface which is the element type of the other. The map type that you pass into the function has to be map[string]IterableWithID. You could create a map of that type, assign values of type MyType to the map, and pass that to the function.
See https://play.golang.org/p/NfsTlunHkW
Also, you probably don't want to be returning a pointer to a slice in MapToList. Just return the slice itself. A slice contains a reference to the underlying array.

Why can methods in Go only be declared on types defined in the same package?

The Go Tour says the following:
You can only declare a method with a receiver whose type is defined in the same package as the method. You cannot declare a method with a receiver whose type is defined in another package (which includes the built-in types such as int).
Is there a reason for this other than avoiding everyone building their own methods off int and string? I've Googled around, but can't find anything referencing it.
The reason is that if you could define methods on other packages' types, you could modify the behavior of other packages. This is because the method set of a given type can have an effect on how values of that type are used.
Consider, for example, the fmt.Println function. When you pass an argument to fmt.Println, it will print a string representation of that value based on a set of rules. One of those rules is that if the type of the value has a String() string method (that is, it implements the fmt.Stringer interface), then that method will be called in order to obtain the string representation of the value.
Thus, imagine that we have a package, foo, and that package has a type, FooInt, defined as follows:
type FooInt int
Now imagine that this package also has a function, PrintFooInt:
func PrintFooInt(f FooInt) { fmt.Println(f) }
This will print the integer value of f. But let's say that you (in a different package, say main) were able to add methods to FooInt. Then you could do this:
func (f FooInt) String() string { return "foobar!" }
This would actually change the behavior of foo.PrintFooInt, which shouldn't be possible from outside the package.

Resources