# Struct aggregation and embedding in golang

A struct,

• is a collection of fields of various types, incuding other structs!
• can be used to create user-defined data types
• can have methods defined on them
• its fields and methods are accessed using the dot notation

For example, Point is a struct which has,

• two fields X and Y of type float64
• has two methods String and MakesNoSense
// structs.go
// Struct definition
type Point struct {
X, Y float64
}

// String overrides how an entity of type Point
// is printed (for example, to stdout)
func (p Point) String() string {
return fmt.Sprintf("Point {%f, %f}", p.X, p.Y)
}

// MakesNoSense is just a dummy method
func (p Point) MakesNoSense() {
fmt.Printf("Point {%f, %f} is just a point!\n", p.X, p.Y)
return
}

func main() {
var pt Point
pt.X = 1.123 //
pt.Y = 2.123 //

fmt.Println(pt)
pt.MakesNoSense()
}


results in,

$go run structs.go Point {1.123000, 2.123000} Point {1.123000, 2.123000} is just a point!  ## Nested Structs Yes, you can define one struct inside another. There are two ways to go about it: struct aggregation and struct embedding. ### Struct Aggregation This approach implies a has-a relationship. For example, a line has two points (start and end) and can be declared as follows: // Line struct has two fields of type Point type Line struct { Start, End Point } // Distance methods calculates the euclidean distance // between the two Points func (l Line) Distance() float64 { xDiff := math.Abs(l.Start.X - l.End.X) yDiff := math.Abs(l.Start.Y - l.End.Y) return math.Sqrt(math.Pow(xDiff, 2) + math.Pow(yDiff, 2)) } // Usage func main() { l := Line{ Start: Point{ X: 2.304, Y: 4.504, }, End: Point{ X: 30.607, Y: 44.104, }, } fmt.Printf("Distance from %v to %v is %f units\n", l.Start, l.End, l.Distance()) } // Distance from Point {2.304000, 4.504000} to Point {30.607000, 44.104000} is 48.674632 units  Let’s redefine Line struct slightly differently using inline structs, type Line struct { Start, End struct { X float64 Y float64 } } // Usage func main() { l := Line{ Start: struct { X float64 Y float64 }{ X: 3.123, Y: 8.123, }, End: struct { X float64 Y float64 }{ X: 4.123, Y: 7.123, }, } }  This approach requires you to rely on anonymous structs during initialization. ### Struct Embedding This approach implies an is-a relationship. For example, a rectangle is a polygon and can be decalred as shown below: // embed.go // Polygon has just two fields for the sake of simplicity type Polygon struct { Width, Height int } // Set width and height of the polygon func (p *Polygon) Set(w, h int) { p.Width = w p.Height = h } // Rectangle is a polygon, with one extra field 'color' type Rectangle struct { color string Polygon // Notice the embedding? } // Area method can access the fields Width and Height even though // they are not directly defined within the Rectangle struct func (r *Rectangle) Area() float64 { return float64(r.Width * r.Height) } func main() { var rect Rectangle rect.Set(10, 20) // direct rect.color = "Blue" fmt.Printf("Rectangle: %+v\n", rect) fmt.Printf("Rectangle Width: %+v\n", rect.Width) // direct fmt.Printf("Rectangle Height: %+v\n", rect.Height) // direct fmt.Printf("Area of the rectangle is: %+v\n", rect.Area()) rect.Polygon.Set(100, 200) // indirect fmt.Printf("Rectangle: %+v\n", rect) }  results in, $ go run embed.go
Rectangle: {color:Blue Polygon:{Width:10 Height:20}}
Rectangle Width: 10
Rectangle Height: 20
Area of the rectangle is: 200
Rectangle: {color:Blue Polygon:{Width:100 Height:200}}


You can see that rect can access:

1. Rectangle struct’s fields and methods
2. Polygon struct’s fields and methods - both directly and indirectly

I am not sure if that’s the correct way to put it, but these are my observations.