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