Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 588
simplebird Avatar asked Oct 25 '16 19:10

simplebird


People also ask

How do I assign one struct to another in Golang?

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.

How do you assign a value to a struct in Go?

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.

How do you use structs in Golang?

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.


2 Answers

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"
like image 84
icza Avatar answered Nov 08 '22 13:11

icza


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.

like image 45
Ainar-G Avatar answered Nov 08 '22 12:11

Ainar-G