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
.
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"