22. Structs

Working with different types together

Struct Type

Slice and array are good for storing collection of data which is of same type. What if our data is made up of smaller types which are different? This is where struct comes in handy.A struct is a type that contains named fields. Struct is a value that is constructed out of other values of many different types.

Slices and maps are used to store collection of similar data where as struct is used to groups together zero or more named values of arbitrary types as a single entity. Grouping together data of different types, that's where struct is useful.

Declaring Struct

A simple struct can be constructed using struct keyword as

struct {field type}

main.go
var person struct {
name string
age int
}

name and age are called fields of struct.

Generally, we will use multiple instance of struct in code. It is very common define struct as user defined type using type keyword for reuse.

main.go
type person struct {
name string
age int
}
var p1 person
var p2 person

now person is a user defined struct type. p1 and p2 variables are both of type person.

Creating struct

Declaring a variable of a particular struct type creates a struct. There is no explicit call required to any other function.

main.go
var p1 person
var p2 person
fmt.Println(p1)
fmt.Println(p2)

Accessing Struct Fields

Struct fields are accessed using dot operator . This can be used to read as well as write to struct fields.

main.go
p1.name = "Joey"
p1.age = 30
fmt.Println(p1)

Zero Values

When a struct is declared, it gets initialized to zero values for its all the fields.

main.go
type show struct {
name string
price float64
isAvailable bool
rating int
}
var s show
fmt.Printf("%#v\n", s)
// output
// main.show{name:"", price:0, isAvailable:false, rating:0}

Struct literals

We can use struct literals to declare and initialize struct with initial value instead of zero value. We can define person struct type using struct literal as

main.go
var p3 person = person{
name: "Chandler",
age: 32,
}
fmt.Printf("%#v\n", p3)

Comparing structs

Two structs of same type are comparable if and only if all the fields of the struct are comparable using ==.

main.go
var p4 person = person{
name: "Chandler",
age: 32,
}
var p5 person = person{
name: "Joey",
age: 30,
}
fmt.Println("are p4 and p5 equal? ==>", p4 == p5)

Struct as another struct field

Like we can use data types like integers ,floats and strings as fields for a struct, we can also user another struct as field for building new structs. This is useful for reusing already created user defined types.

main.go
type human struct {
name string
age int
}
type superHuman struct {
human human
power string
}
superman := superHuman{
human: human{
name: "Clark Kent",
age: 30,
},
power: "Flying",
}
fmt.Printf("%#v\n", superman)
// output
// main.superHuman{human:main.human{name:"Clark Kent", age:30}, power:"Flying"}

Note that if we need to access name of superman we need to use superman.human.name

main.go
fmt.Println(superman.human .name)

Anonymous fields for struct

What if we wish to use superman.name instead of superman.human.name in above code? Go supports this by providing type embedding. An inner struct that is stored within an outer struct using an anonymous field is said to be embedded within the outer struct.

We are embedding human type inside superHero type. There is no named field inside superHero to which we are assigning human type. Outside struct literal, we can access fields of human type directly as if these fields exists on superHero. This is called type promotion.

main.go
type superHero struct {
livesSaved int
human // embedding type
}
batman := superHero{}
ironman := superHero{
livesSaved: 100000,
human: human{
name: "Tony",
age: 40,
},
}
batman.name = "Bruce" // accessing embedded type's field
batman.age = 50 // accessing embedded type's field
batman.livesSaved = 100
fmt.Printf("%#v\n", batman)
fmt.Printf("%#v\n", ironman)

Embedding Types

Blog Post