simplebird
simplebird

Reputation: 131

Golang: Type assign with another struct

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.

Upvotes: 12

Views: 6448

Answers (3)

icza
icza

Reputation: 417412

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"

Upvotes: 20

Ainar-G
Ainar-G

Reputation: 36189

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.

Upvotes: 7

Volker
Volker

Reputation: 42433

The whole (and only) reason to do type Form Rectangle is to define a new type with different methods. In your example: No methods. c is a Form and has no methods, it's only raison d'être is not having a SomethingElse() method.

But a Form still embeds a Circle which is accessible as c.Circle and which is a Circle so it obviousely has the method Something().

Upvotes: 1

Related Questions