In the previous tutorial, we have seen how to use pointers in Golang. In this tutorial, we will see how to use structures in Golang. Structs (short for structure) is a user defined data type that groups zero or more variables of different types into a single type. Each value in a struct is called a field. In this article, we will explore structures in depth with a lot of practical examples.
In order to follow through this article:-
- You need to have a basic understanding of programming.
- You need to download the Go compiler from the official website and follow the directions to install it based on your operating system being either Windows, MacOS or any of the Linux distributions.
- You need a code editor like visual studio, atom or sublime text. Alternatively, you can download an Integrated Development Environment (IDE). An IDE is an improved code editor with added qualities customized to help you write and execute codes easier. Goland is the most popular IDE used for developing Go programs.
- You need to have an understanding of Go basic data types like int, strings and float, as well as pointers.
How to use Structures in Golang [Complete Tutorial]
Also Read: How to Use Pointers in Golang [Complete Tutorial]
Struct is a user-defined type that stores data as a collection of fields. Structs are used to represent any real world entity that has properties peculiar to it. For example, a student possesses properties like:-
- First Name
- Last Name
- Student ID
Instead of defining single variables for these properties, we can define them as fields within a single struct.
In the code above, we use the type keyword to define a new data-type. Student represents the identifier of the type which is a struct. Within the curly braces, we define all fields by their identifier and data type. Each field within a struct must have a unique identifier.
We can group similar fields with the same type by defining them on the same line. In the struct above, Firstname and Lastname are similar and have the same data type, so they can be written on the same line. Although this is valid, it is not recommended since it reduces code readability.
We declare a variable of type struct using the var keyword. After a struct is declared, each field is initialized with the zero value of its respective data types. We can also initialize a struct during its declaration using struct literals.
64-bit Ubuntu 20.04Linux environment.
In the program above, notice that the fields of the variable Mary are not initialized in the same order as they were declared. This is possible because we initialized each field with its Key and Value. We can instead define the fields with only its values, but it must be in the same order as it was declared. The fields of a single struct cannot be initialized in both ways.
We can also declare and define a struct within a local scope. This is called an anonymous struct since it does not declare a type that can be used to declare other variables. An anonymous struct is declared, initialized and stored inside a variable within a function.
We can access any field of a struct using the dot notation (.). For example, we may declare a struct and then use the dot notation to initialize each struct field with a value. We can also read and update each struct field using dot notation by calling a field in the format <variable_name>.<field_name>.
On line 27 in the program above, the “#” character formats the struct to print each field with its key and value separated by a colon.
Exporting Structs and Fields
Exporting a struct means making it available in a different package. In Go, a struct can be exported by capitalizing the first character of its identifier. The same applies to each field of a struct. A single struct can contain both exported and unexported fields.
In order to understand this better, lets create a code sample with multiple packages:-
1. Create a folder/directory called structs within any directory of your choice,
2. Create another directory called student within structs.
3. Within student, create a student.go file and input the following program into it:
In the code snippet above, we created a new package student and defined an exported struct type Student. We exported the fields FirstName, LastName, Age and Department, while school and studentID are not exported.
4. In the structs directory, create a go module by running go mod init structs. The result should be in the form:-
5. Create a main.go file in structs directory and write the following program:-
The structs directory should now have the structure:-
In the code snippet above, on line 5, we import the student package. Within main(), we created a variable of type Student from the student package and initialized a value for its exported fields. If we run the code from structs using go run main.go, we get no errors with the following result:-
If we try to initialize one or both of the unexported fields, the compiler returns an error. Modify main.go file to the code below:-
Because this source code is on a Visual Studio text editor, its built-in intelligent alerts us of the error. Note that when running on some text editors, the editor will not notify you of the errors until you run the code using go run main.go. Running the code results in the following compilation error:-
The example we just showed can be applied in a school management system where studentID and the school fields do not need to be modified by students. Note that we can define an unexported struct within the student package, and whether its fields are exported or not, the struct will not be accessible from main.
Pointer to Structs
A struct variable is like every other variable, therefore, we can store its address in a pointer. Go allows us to define a pointer to a struct by adding an address-of operator (&) at the front of a struct literal.
We can access each field of a struct pointer by using the dot notation after dereferencing the struct pointer. But Go allows us to neglect the dereferencing operator (*) when accessing a struct pointer field. The compiler dereferences the struct pointer underneath.
Anonymous Struct Fields
As we discussed above, a struct could be named or anonymous. The same applies to a struct field. We can assign an identifier to a struct field or assign just its data type. In the case where only the data type is specified, the struct field will have an implicit identifier which is the same as its data type.
Though the program above is valid, we do not apply anonymous struct fields in that way. Instead, anonymous struct fields are applied where two or more structs possess similar fields. For example, in the school management software we highlighted above, we may want to create a struct type to represent qualities peculiar to teachers.
Notice that Student and Teacher both define these three fields:-
We can group these three fields into a single struct and call it Person since both teachers and students are people. Then we embed Person into Teacher and Student structs as shown below.
We can define variables of type Teacher and Student in the following way:-
We can apply anonymous fields to the Student and Teacher structs if we omit the identifier of Person within them. This allows the Person fields to be promoted and accessed as if they are defined in Student. Let's dive into code to demonstrate how this works.
From lines 41, 42, 57 and 58 above, the lastName and firstName fields were returned as if they were declared within Student and Teacher respectively. This concept is called composition. Note that when defining the variables mary and harry, the key of Person struct is the same as its data type Person.
Two struct variables are comparable if their corresponding fields are comparable. Slices and maps are not comparable. Therefore, a struct containing fields of slice and map types are not comparable. Two structs are considered equal if their corresponding field values are equal.
Embedding anonymous struct fields also allows the parent struct to inherit the methods of an embedded struct. Struct is one of the most important data structures used in programming. The concept of structs is usually compared to classes in other languages. It is the basis of writing object oriented code in Go.