Uncategorized

Embedding in Go

Go supports defining is-a relationships using an embedded type.

Fields and methods in a struct have a has-a relationship

E.g.  Person has-a name and email.

type Person struct {
	Name   string
	Email  string
}

Embedding to define an is-a relationships  is declared with a type but no explicit field name. The unqualified type name acts as the field name.

type Employee struct { 
      Person 
      EmployeeID string 
}

This way the Person struct can be accessed using the type name:

accountant := new(Employee)
accountant.Person.Pay()

We can also invoke any Person methods directly on the Employee object

accountant := new(Employee)
accountant.Pay()

We cannot embed a slice or a map

type Employee struct { 
 []Person
 EmployeeID string 
}

This will give  a syntax error: unexpected [, expecting field name or embedded type

A workaround will be defining a type and then embedding

type SpecialPeople []Person

type Employee struct { 
 SpecialPeople
 EmployeeID string 
}

The spec in Go describes embedding as below:

EmbeddedField = [ "*" ] TypeName .

Embed by-pointer

The advantage of embedding by reference is that  you are embedding all the functionality of a type without needing to know when it is instantiated. The major application of this would be to have thousands of instances sharing a single underlying data structure. This can significantly reduce memory consumption.

type Image struct {
     data [5][5]
}

type Block struct {
   *Image 
   show bool
}

Because, the unqualified type name acts as the field name for an embedded struct, we can’t have an embedded struct and its pointer it the same struct

struct {
	T     // conflicts with embedded field *T and *P.T
	*T    // conflicts with embedded field T and *P.T
	*P.T  // conflicts with embedded field T and *T
}

Promotion of fields or methods

All field or method calls for embedded type objects are resolved at compile-time without the use of a virtual table. A field or method of an embedded field in a struct is called promoted.

type Person struct { 
      Name   string 
      Email  string 
}
func (p *Person) FreeGift() int64{ 
     return 100 
}

type Employee struct {       
     Person       
     EmployeeID string  
}

In this example person.FreeGift()  and employee.FreeGift() is the same so the method is promoted. Promotion  occurs only at the first level.

https://play.golang.org/p/N8rXyIR_CW

However, if we have a method with the same name defined on the parent struct as below, then employee.FreeGift() will invoke the method on the Employee struct

func (e *Employee) FreeGift() int64{ 
      return 200
}

In the above example, calling person.FreeGift() and employee.Person.FreeGift() will invoke the method on the Person struct

https://play.golang.org/p/lYUTijVF7U

One very important design aspect to remember is, if you plan to use an embedded struct and the embedded type has non-exported fields or methods,  those are completely inaccessible to you in a separate package.

Embedding Interfaces

Embedding an interface will add all (exported and non-exported) methods of the embedded interface to the enclosing interface.

type ImageWriter interface { 
      Read(b Buffer) bool 
      Write(b Buffer) bool 
} 
type ImageFile interface { 
     ImageWriter  // same as adding the methods of ImageWriter 
     Close() 
}

 

 


			

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s