— golang — 1 min read
A struct,
For example, Point
is a struct which has,
X
and Y
of type float64
String
and MakesNoSense
1// structs.go2// Struct definition3type Point struct {4 X, Y float645}6
7// String overrides how an entity of type `Point` 8// is printed (for example, to `stdout`)9func (p Point) String() string {10 return fmt.Sprintf("Point {%f, %f}", p.X, p.Y)11}12
13// MakesNoSense is just a dummy method14func (p Point) MakesNoSense() {15 fmt.Printf("Point {%f, %f} is just a point!\n", p.X, p.Y)16 return17}18
19func main() {20 var pt Point21 pt.X = 1.123 // 22 pt.Y = 2.123 // 23
24 fmt.Println(pt)25 pt.MakesNoSense()26}
results in,
1$ go run structs.go2Point {1.123000, 2.123000}3Point {1.123000, 2.123000} is just a point!
Yes, you can define one struct inside another. There are two ways to go about it: struct aggregation and struct embedding.
This approach implies a has-a
relationship. For example, a line has two points (start and end) and can be declared as follows:
1// Line struct has two fields of type Point2type Line struct {3 Start, End Point4}5
6// Distance methods calculates the euclidean distance 7// between the two Points8func (l Line) Distance() float64 {9 xDiff := math.Abs(l.Start.X - l.End.X)10 yDiff := math.Abs(l.Start.Y - l.End.Y)11
12 return math.Sqrt(math.Pow(xDiff, 2) + math.Pow(yDiff, 2))13}14
15// Usage16func main() {17 l := Line{18 Start: Point{19 X: 2.304,20 Y: 4.504,21 },22 End: Point{23 X: 30.607,24 Y: 44.104,25 },26 }27 fmt.Printf("Distance from %v to %v is %f units\n", l.Start, l.End, l.Distance())28}29// 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,
1type Line struct {2 Start, End struct {3 X float644 Y float645 }6}7
8// Usage9func main() {10 l := Line{11 Start: struct {12 X float6413 Y float6414 }{15 X: 3.123,16 Y: 8.123,17 },18 End: struct {19 X float6420 Y float6421 }{22 X: 4.123,23 Y: 7.123,24 },25 }26}
This approach requires you to rely on anonymous structs
during initialization.
This approach implies an is-a
relationship. For example, a rectangle is a polygon and can be decalred as shown below:
1// embed.go2// Polygon has just two fields for the sake of simplicity3type Polygon struct {4 Width, Height int5}6
7// Set width and height of the polygon8func (p *Polygon) Set(w, h int) {9 p.Width = w10 p.Height = h11}12
13// Rectangle is a polygon, with one extra field 'color'14type Rectangle struct {15 color string16 Polygon // Notice the embedding?17}18
19// Area method can access the fields Width and Height even though20// they are not directly defined within the Rectangle struct21func (r *Rectangle) Area() float64 {22 return float64(r.Width * r.Height)23}24
25func main() {26 var rect Rectangle27 rect.Set(10, 20) // direct28 rect.color = "Blue"29 fmt.Printf("Rectangle: %+v\n", rect)30 fmt.Printf("Rectangle Width: %+v\n", rect.Width) // direct31 fmt.Printf("Rectangle Height: %+v\n", rect.Height) // direct32 fmt.Printf("Area of the rectangle is: %+v\n", rect.Area())33
34 rect.Polygon.Set(100, 200) // indirect35 fmt.Printf("Rectangle: %+v\n", rect)36}
results in,
1$ go run embed.go2Rectangle: {color:Blue Polygon:{Width:10 Height:20}}3Rectangle Width: 104Rectangle Height: 205Area of the rectangle is: 2006Rectangle: {color:Blue Polygon:{Width:100 Height:200}}
You can see that rect
can access:
Rectangle
struct's fields and methodsPolygon
struct's fields and methods - both directly and indirectlyI am not sure if that's the correct way to put it, but these are my observations.
Note: This article is not an in-depth tutorial or treatment of Golang's syntax, semantics, design or implementation, but a journal of my learnings.