9. Structs
A struct is a collection of fields and is defined with the type and struct keywords.
An example:
1 type myStructName struct {
2 x int
3 y string
4 }
The type keyword introduces a new type. It is followed by the name of the type (myStructName), the keyword struct to indicate that we are defining a struct type and a list of fields inside of curly braces. Each field has a name and a type. Like with functions we can collapse fields that have the same type:
1 type Circle struct {
2 x, y, r float64
3 }
Struct fields are accessed using a dot. Here’s an example:
Program: struct1.go
1 package main
2
3 import "fmt"
4
5 // this struct is visible only in this package
6 // as it starts with a small letter
7 type myStructName struct {
8 // variables starts with small letter,
9 // so NOT visible outside package
10 x int
11 y string
12 }
13
14 func main() {
15 msn := myStructName{1, "talim"}
16 msn.x = 4
17 fmt.Println(msn.x)
18 }
The output is is 4.
Let us define a Square struct as follows:
Program: struct2.go
1 package main
2
3 import "fmt"
4
5 // this struct is visible outside this package
6 // as it starts with a capital letter
7 type Square struct {
8 // variable X starts with a capital letter,
9 // so visible outside package
10 X int
11 name string
12 }
13
14 func main() {
15 sqr := Square{}
16 fmt.Println("Default square is: ", sqr)
17 }
When you run the example, the output is Default square is: {0 }
This means that in a struct the values of the variables by default for an int will be 0; a string will be empty, etc.
Now let us look at different ways of initializing a struct and setting values for variables within it.
Program: struct3.go
1 package main
2
3 import "fmt"
4
5 type Square struct {
6 X int
7 name string
8 }
9
10 func main() {
11 //initialize values in order they are defined in struct
12 sqr1 := Square{10, "Go_Sqr1"}
13 fmt.Println("Square sqr1 is : ", sqr1)
14
15 //initialize values by variable name in any order
16 sqr2 := Square{name: "Go_Sqr2", X: 15}
17 fmt.Println("Square sqr2 is : ", sqr2)
18
19 //get pointer to an instance with new keyword
20 ps := new(Square)
21
22 //set value using . notation by dereferencing pointer
23 (*ps).X = 30
24
25 //set value using . same as above
26 ps.name = "Go_Sqr3"
27
28 fmt.Println("Square ps is : ", *ps)
29
30 ps1 := &sqr1
31 ps1.X = 22
32 fmt.Println("The new value of X: ", ps1.X)
33 fmt.Println("Square sqr1 is : ", sqr1)
34 }
The output is:
Square sqr1 is : {10 Go_Sqr1}
Square sqr2 is : {15 Go_Sqr2}
Square ps is : {30 Go_Sqr3}
The new value of X: 22
Square sqr1 is : {22 Go_Sqr1}
Note: As seen above, you can get a pointer to a newly created struct instance using the new keyword. A pointer thus obtained, can be used with or without using the * operator to get variables within it.
Thus new(T) returns a pointer to a newly allocated zero value of type T. This is important to remember.
This means a user of the data structure can create one with new and get right to work.
Another example:
Program: struct_ptr.go
1 package main
2
3 import "fmt"
4
5 type Person struct {
6 name string
7 age int
8 }
9
10 func main() {
11 fmt.Println(Person{"Kiran", 30})
12 fmt.Println(Person{name: "Satish", age: 60})
13
14 s := Person{"Victor", 45}
15 fmt.Println(s)
16
17 r := &Person{"Harry", 30} // Pointer
18 fmt.Println(r)
19 r.age = 33 // Changing the values
20 fmt.Println(*r) // Pointer de-referencing
21
22 // Another example of Pointers
23 p := &Person{}
24 p.name = "Hulk"
25 p.age = 10
26 fmt.Println(*p)
27 }
The output is:
{Kiran 30}
{Satish 60}
{Victor 45}
&{Harry 30}
{Harry 33}
{Hulk 10}
An anonymous struct field
Program: struct4.go
1 package main
2
3 import "fmt"
4
5 type A struct {
6 ax, ay int
7 }
8 type B struct {
9 A
10 bx, by float64
11 }
12
13 func main() {
14 b := B{A{1, 2}, 3.0, 4.0}
15 fmt.Println(b.ax, b.ay, b.bx, b.by)
16 fmt.Println(b.A)
17 }
The output is:
1 2 3 4
{1 2}
Anonymous fields of any type
Any named type, or pointer to one, may be used in an anonymous field and it may appear at any location in the struct.
Program: struct5.go
1 package main
2
3 import "fmt"
4
5 type C struct {
6 x float64
7 int
8 string
9 }
10
11 func main() {
12 c := C{3.5, 7, "hello"}
13 fmt.Println(c.x, c.int, c.string)
14 }
The output is:
3.5 7 hello
Now let us understand how to define a function on a struct.
A normal function that we name myFunc that takes no parameters and returns an int would be defined similar to the code snippet shown below:
1 func myFunc() int {
2 //code
3 }
A function like myFunc that takes no parameters and returns an int, but which is associated with a type which we call as myType would be defined similar to the code snippet shown below:
1 type myType struct{}
2
3 func (f myType) myFunc() int {
4 //code
5 }
In between the keyword func and the name of the function (myFunc()) we have added a “receiver”. The receiver is like a parameter – it has a name and a type. In Go, a function which takes a receiver is usually called a method.
Let’s extend our earlier Square struct to add an Area function. This time we will define that the Area function works explicitly with the Square type:
Program: struct6.go
1 package main
2
3 import "fmt"
4
5 type Square struct {
6 X int
7 name string
8 }
9
10 func (s Square) Area() int {
11 return s.X * s.X
12 }
13
14 func main() {
15 r1 := Square{5, "my_sqr"}
16 fmt.Println("Square is: ", r1)
17 fmt.Println("Square area is: ", r1.Area())
18 }
The output is:
Square is: {5 my_sqr}
Square area is: 25
|
Exercise 14Create a simple stack Go program which can hold a fixed number of ints.
It does not have to grow beyond this limit. Define |
9.1 Solutions
- https://play.golang.org/p/ya7p5kOvom↩