So I have this example here: Go Playground
package main
import (
"fmt"
)
type Circle struct{}
func (c Circle) Something() {
fmt.Println("something")
}
type Rectangle struct {
Circle
}
func (a Rectangle) SomethingElse() {
fmt.Println("SomethingElse")
}
type Form Rectangle
func main() {
c := Form{}
c.Circle.Something()
c.SomethingElse()
}
I don't understand why I can call Something
from the embedded Circle
, but cannot call Somethingelse
from the Rectangle
within the Form
type. Also I don't understand what benefit I get when I declare a type of some other type, like here in Form
.
A struct variable in Golang can be copied to another variable easily using the assignment statement(=). Any changes made to the second struct will not be reflected back to the first struct.
Default values can be assigned to a struct by using a constructor function. Rather than creating a structure directly, we can use a constructor to assign custom default values to all or some of its members. Another way of assigning default values to structs is by using tags.
A structure or struct in Golang is a user-defined type that allows to group/combine items of possibly different types into a single type. Any real-world entity which has some set of properties/fields can be represented as a struct. This concept is generally compared with the classes in object-oriented programming.
This:
type Form Rectangle
Creates a new type named Form
, having Rectangle
as its underlying type.
This means that the fields of Rectangle
(which is a struct) will be defined for Form
as well.
But methods are bound to a specific type. When you create a new type (Form
), that new type will not have any of the methods of its underlying type, so you can't call c.SomethingElse()
as SomethingElse()
is a method of the Rectangle
type.
c.Circle.Something()
works, because c.Circle
is a field of type Circle
, and Something()
is a method of the Circle
type.
If you want to call the Rectangle.SomethingElse()
method, that requires a value of type Rectangle
(the receiver type is Rectangle
). Since underlying type of Form
is Rectangle
, you can simply obtain a value of type Rectangle
from a value of type Form
using a simple type conversion:
Rectangle(c).SomethingElse() // This works
The benefit of creating a new type is that so you can create / add your own methods for it. A common example is implementing the sort.Interface
interface. Let's say you have a slice of something, e.g. []Rectangle
, or a slice of some type which you have no control over (beause it's part of another package – and methods for a type can only be defined in the same package). If you want to sort this slice, you create a new type for which you can define methods, the methods of sort.Interface
, e.g.:
type SortRectangle []Rectangle
func (s SortRectangle) Len() int { return len(s) }
func (s SortRectangle) Less(i, j int) bool { return s[i] <some-logic> s[j] }
func (s SortRectangle) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
The sort.Sort()
function is able to sort any values that implement sort.Interface
. The []Rectangle
does not, but we just created a new type SortRectangle
which does. And if we have a value of type []Rectangle
, we can convert it to SortRectangle
because the former is the underlying type of the latter, and by doing a conversion, we have a value of type SortRectangle
which can be passed to sort.Sort()
, in order to have it sorted:
rs := []Rectangle{}
// Sort rs:
sort.Sort(SortRectangle(rs))
Note that a conversion like the above SortRectangle(rs)
only changes the runtime type information, it does not change the memory representation of rs
, so it's pefectly safe and efficient.
If you want the new type to have the methods of the "old" type, then use embedding. See Ainar-G's answer. In fact, you already did this by embedding Circle
in Rectangle
: the type Rectangle
has a method Something()
, because Something()
is a method of Circle
:
Rectangle{}.Something() // Prints "something"
A simple rule in Go. If you want the type's methods, do
type A struct { B }
and if you don't want the type's methods, do
type A B
Why do we need the second form? Interfaces, for example. Sometimes we don't want a value to satisfy an interface, like here. Other times you just need the types and not its methods.
Go gives you the possibility to get the type that is the same, but with an empty method set.
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