Structs in Go
What is a struct?
In the previous article, we learned about maps, and we saw different examples of maps and understood why they are such a useful data structure when it comes to storing some kind of data, but they too have some limitations.
Maps don't allow us to store data of multiple types. Also, there are several constraints in a map when it comes to storing keys as well. For such reasons, it can be said that maps are not the most convenient way to pass data from one function to another in Go. And that's where Structs come into the picture in Go.
Structs are a user-defined type in Go that store different types of data, and the data stored in a struct is called fields, where each field can be of different types if we want. They are ideal to use when we want to group data of similar types together.
It is true that a struct can be compared with a class that we have in object-oriented programming languages, but they aren't exactly the same. The difference is quite simple actually, as in Go we don't have classes, as it doesn't have the inheritance. Go does have some features of object-oriented languages, but they are done in a different manner.
A common example of a struct would be a struct for Employee data, where there would be fields with employee firstName
, lastName
, age
, employeeID
etc. We can group these four properties into a single struct and call that struct as EmployeeData
.
Now, that we know a little about what structs are, let's try to declare one.
Declaring a struct
Before we declare a struct, let's first understand what is the actual syntax for the same.
A struct type mainly contains a blueprint of the data that the structure will hold. To declare a struct we need a syntax similar to the one shown below.
type EmployeeData struct {
firstName string
lastName string
age int
employeeID int
}
In the above code snippet, we created a struct named EmployeeData
that contain four fields inside it. The first field is the firstName
of the employee, the second one is the last name, then the age
and lastly the employee ID.
It should be noted that the above struct is a named struct as it creates a new data type named EmployeeData
that can be used to create EmployeeData
structs.
We can also improvise a little when it comes to the above declaration, as the field that has the same data type can be placed in a single line.
Consider the code snippet shown below.
type EmployeeData struct {
firstName, lastName string
age, employeeID int
}
The above struct is exactly the same as the previous one when it comes to its functionality. Though it is generally recommended to make use of the previous syntax when it comes to struct declaration as fields in the above syntax are not that explicit.
Named structs
In the previous section, we learned how we can declare structs in Go. In this section, we will make use of that syntax to create named structs.
Consider the code shown below that depicts the case where we are creating two structs with the EmployeeData
struct syntax.
type EmployeeData struct {
name string
age int
id int
}
emp1 := EmployeeData{name: "Mukul Latiyan", age: 24, id: 121}
emp2 := EmployeeData{name: "Mayank Agarwal", age: 24, id: 111}
fmt.Println(emp1)
fmt.Println(emp2)
In the above code, we declared two named structs, emp1
and emp2
respectively, and then we gave the fields some values according to the data type and lastly we printed these two structs with the Println()
function.
It should be noted that when we are specifying fields for each field name, then the order of the fields is not important as long as we provide the field name too. In such a case, we can even change the position of the fields and the compiler won't complain at all.
If we run the above code with the command go run struct.go
then we will get the following output in the terminal.
{Mukul Latiyan 24 121}
{Mayank Agarwal 24 111}
In the above example, we provided the field names as well, but we can omit that when initializing a struct and we must maintain the order in such a case, otherwise the compiler will throw an error.
Consider the code shown below where we create a named struct and don't make use of the field names when initializing it.
emp2 := EmployeeData{"Mayank Agarwal", 24, 111}
fmt.Println(emp2)
Notice how the field names are not present inside the emp2
named struct initialization. It is recommended that we don't use this type over the field name one as it is more prone to errors.
If we run the above code with the command go run struct.go
then we will get the following output in the terminal.
{Mayank Agarwal 24 111}
Accessing individual fields of a struct
We can access individual fields of a struct by making use of the dot(.
) operator syntax.
Consider the code shown below where we will create a named struct and then print each field of that struct with the help of the dot operator.
type person struct {
firstName string
lastName string
age int
id int
}
func main() {
// Create a new person struct
p := person{firstName: "Mayank", lastName: "Agarwal", age: 24, id: 111}
// Accessing individual fields using dot operator
fmt.Println(p.firstName)
fmt.Println(p.lastName)
fmt.Println(p.age)
fmt.Println(p.id)
}
In the above code, we are printing all the field values using the dot operator.
If we run the above code with the command go run struct.go
then we will get the following output in the terminal.
Mayank
Agarwal
24
111
Zero value of a struct
When we define a struct but we don't initialize the fields with any value, then if we access those fields of the struct with the dot operator, we will be greeted with zero values of the fields. As we learned that zero values of different data types are different,
Consider the code shown below where we will declare a struct but won't initialize its fields with any values.
type EmployeeData struct {
firstName string
lastName string
age int
salary int
}
In the above code, the line emp2 := EmployeeData{}
is the one where we are declaring a struct but hasn't provided any value to the field and then following that we are printing the values of all the fields that are present in the struct named EmployeeData
.
If we run the above code with the command go run struct.go
then we will get the following output in the terminal.
0
0
The first two values of the above output are empty strings and the last two are zero.
Go also allows us to specify the values of some fields and ignore the rest of the fields.
Consider the code shown below where we will create a struct whose few fields will be initialized and the others won't be.
emp1 := EmployeeData{
firstName: "Mukul",
age: 24,
}
Notice, how in the above code we initialized only the firstName
and the age field of the struct named EmployeeData
and then simply ignored the initialization of the other fields.
If we run the above code with the command go run struct.go
then we will get the following output in the terminal.
Mukul
24
0
Anonymous structs
In Go, we can declare structs without having to create a new data type. The idea is that we make use of the struct keyword followed by the immediate initialization of all the fields of the struct.
Consider the code shown below where we are creating an anonymous struct and assigning values to its fields at the same time.
emp2 := struct {
firstName string
lastName string
age int
empID int
}{firstName: "Mukul", lastName: "Latiyan", age: 24, empID: 121}
In the above code, the line emp2 := struct{}
is the one where we are declaring an anonymous struct variable.
It should be noted that a struct is called anonymous when it doesn't create a new data type, which is what we did in the code above, as we only created a new struct variable.
If we run the above code with the command go run struct.go
then we will get the following output in the terminal.
{Mukul Latiyan 24 121}
Anonymous fields
In the previous section, we learned about anonymous structs and saw how we can create one. Go also provides us with an option to declare anonymous fields in a struct.
Anonymous fields are those fields that don't have a name attached to them.
The below code snippet shows a struct named EmployeeData
where there are three anonymous fields in it.
type EmployeeData struct {
string
int
bool
}
The above struct contains three fields, all of them are anonymous in nature. The name of the anonymous field is the name of its type and we can use that name to access these values and to also assign values.
Consider the code shown below where we will make use of a struct with different anonymous fields and then assign values to all these fields before printing each field with the help of the dot operator.
empData := EmployeeData{"Mukul", 24, true}
In the above program, we are accessing the anonymous fields of the struct with the help of the types as the field names which are string
, int
and bool
respectively.
If we run the above code with the command go run struct.go
then we will get the following output in the terminal.
Mukul
24
true
Pointers to a struct
We can also create pointers to a struct when initializing a struct. To create a pointer to a struct, we make use of the address-of operator(&
).
Consider the code shown below where we will create a pointer to a struct.
type EmployeeData struct {
firstName string
age int
}
func main() {
emp1 := EmployeeData{"John", 30}
emp2 := &EmployeeData{"Mukul", 24} // creating a pointer to struct using & operator
fmt.Println((*emp2).firstName) // de-referencing the pointer to access the field
fmt.Println(emp2.age) // accessing field without de-referencing the pointer
}
emp2
in the above program is a pointer to the EmployeeData
struct. When we are accessing a particular field of the struct with the help of the emp2
variable, we need to de-reference the variable to do so. To de-reference a variable in Go, we make use of the asterisk(*
) operator, that's why we wrote (*emp2).firstName.
If we run the above code with the command go run struct.go
then we will get the following output in the terminal.
Mukul
24
In Go, we can make use of the standard syntax emp2.firstName
to access a field of a pointer to struct variable instead of having to use (*emp2).firstName
.
Consider the code shown below where we will make use of emp2.firstName
instead of (*emp2).firstName
.
type EmployeeData struct {
firstName string
age int
}
func main() {
emp1 := EmployeeData{"John", 30}
emp2 := &EmployeeData{"Mukul", 24}
fmt.Println(emp2.firstName) // accessing field of a pointer to struct variable without using the de-referencing operator
fmt.Println(emp2.age)
}
If we run the above code with the command go run struct.go
then we will get the following output in the terminal.
Mukul
24