Sebastian Gomez
Structs in Go
In Go, a struct (struct) is a collection of variables (fields) grouped under one name. Structs are fundamental for organizing and handling related data efficiently. Unlike other languages that use classes, Go relies on structs to model data and behavior.
Here is an example of a struct in Go:
// Defines a new type called 'Persona'.
type Persona struct {
// Declares a 'nombre' field of type string inside the 'Persona' struct.
nombre string
// Declares an 'edad' field of type int (whole number) inside the 'Persona' struct.
edad int
}In the example above, we have a struct called Persona. This struct has two fields, nombre and edad, of type string and int, respectively.
Now, to use this struct in our code, we need to create an instance of it. We can do that as follows:
// Both forms work exactly the same
miPersona := Persona{nombre: "Juan", edad: 25}
var miPersona Persona = Persona{nombre: "Juan", edad: 25}In the example above, we created an instance of the Persona struct named miPersona. This instance holds the values "Juan" and 25 for the nombre and edad fields, respectively.
To access the fields of a struct, you can use the dot operator (.). For example, if we wanted to print the data in miPersona, we can use the following code:
fmt.Println("Nombre:", miPersona.nombre)
fmt.Println("Edad:", miPersona.edad)Besides fields, structs can also have associated methods, though not inside their definition. This is a particularity of Go, since the method is added to the struct after its declaration. For example, we can add the following method to the Persona struct:
func (p Persona) saludar() string {
return "Hola, mi nombre es " + p.nombre
}Now, to use this method, we can call it like this:
mensaje := miPersona.saludar()Note: The difference between a plain function and a method lies in the parentheses where you write which struct it belongs to, and you should not confuse those with the parameters the method receives. For example, func (p Persona) saludar() is different from func saludar(p Persona), because the first set of parentheses after the word func indicates which struct the method belongs to; otherwise, it would be a normal function.
In the example above we called the saludar method of miPersona and stored the result in the mensaje variable. The result would be the following:
"Hola, mi nombre es Juan"Structs versus classes
Go does not have classes. Instead, Go has structs that you can use to model data and behavior. Structs are defined with the struct keyword. Although Go does not have classes, some object oriented programming concepts, such as inheritance, can be simulated using structs.
Another difference is that structs do not have inheritance, whereas classes do. This means a struct cannot inherit properties or methods from another struct, while a class can inherit properties and methods from another class.
Structs are passed by value. This means any change made to a copy of a struct will not be reflected in the original struct.
Some examples of structs in Go
Car:
type Coche struct {
// Declares a 'marca' field of type string inside the 'Coche' struct.
marca string
// Declares a 'modelo' field of type string inside the 'Coche' struct.
modelo string
// Declares an 'anio' field of type int (whole number) inside the 'Coche' struct.
anio int
}Student:
type Estudiante struct {
nombre string
edad int
curso string
notas [3]float64
}Composite structs
In Go, we can compose structs by embedding one inside another. One point is worth clarifying: unlike in other languages, this is not true nesting, but rather what Go calls field promotion. When we embed a type inside a struct, its fields and methods are promoted and become accessible from the outer struct. For example, we can create a struct called Tractor that embeds a struct called Carro:
type Carro struct {
brand string
year int
}
type Tractor struct {
Carro
capacity int
}In the example above, the Tractor struct contains a field called capacity and an embedded Carro struct. That embedded struct provides the brand and year fields.
To access the fields of the embedded type we can use the dot operator (.). When we embed a type without giving it a field name, the promoted field takes the name of the type itself. So, to access the brand field through an instance of Tractor, we use the name of the embedded type, Carro:
miTractor := Tractor{Carro{brand: "Ford", year: 2022}, 2}
fmt.Println("------------------------")
fmt.Println("Tractor:", miTractor.Carro.brand)Anonymous fields in structs
In Go it is possible not to declare the field name in our struct and provide only the data type. Done this way, the field takes the name of the data type, and we can access it using that name. Let's look at an example that compiles:
type Videogame struct {
string
int
}
func main() {
myVideogame := Videogame{string: "Titulo", int: 2017}
fmt.Println(myVideogame)
// prints {Titulo 2017}
}In this case the fields are literally named string and int, because that is the name the anonymous field takes from its type. That is why the keyed literal {string:..., int:...} works, the keys match the promoted field names.
Access modifiers in Go
Unlike other programming languages, such as Java and Python, which use access modifiers like public, private, or protected to specify scope, Go determines whether an element is exported or unexported based on how it is declared. Exporting an element, in this case, makes it visible outside the current package. If it was not exported, it is only visible and usable within the package where it was defined.
This external visibility is controlled by capitalizing the first letter of the declared element. Every declaration, such as types, variables, constants, functions, and so on, that starts with an uppercase letter is visible outside the current package.
Conclusions
In this post we explored the concept of structs in the Go programming language. We learned that structs are collections of related data that let us store and organize information efficiently. We also saw how to define, instantiate, and use structs in Go, as well as the ability to add associated methods to them. We highlighted some important differences between structs in Go and classes in other languages, such as the absence of inheritance and pass by value semantics. Finally, we saw how to compose structs through embedding (field promotion) and how to use anonymous fields, along with using uppercase letters to control the visibility of elements in Go. In short, structs are a fundamental part of programming in Go, since they let us organize and manipulate data in an effective and flexible way.
Suggested exercises
Below I leave you some exercises so you can practice structs in Go and submit your solution as a Pull Request to this repository, Go Para Principiantes.
- Create an
EquipoDeportivostruct that holds information about players, coaches, and matches played. - Create a
Supermercadostruct that holds information about products, employees, and sales. - Create a
Universidadstruct that holds information about students, professors, and courses offered. - Create a struct called
Librowith fields likeTitulo,Autor, andAnioDePublicacion. Then instantiate alibro1object and display its data. - Define a method on the
Cochestruct that computes the average speed based on the distance traveled and the time. Then create a car instance and use this method to compute the average speed. - Create an
Escuelastruct with anombrefield and a slice ofEstudiantestructs, where each student has anombre,edad, andcurso. Create anEscuelainstance with several students and display their data. - Define a struct called
Productothat has an anonymous field of typePreciowith avalorfield of typefloat64. Then create a product and display its price. - Simulate inheritance in Go by creating an
Animalstruct with fields likenombreandedad, and then createPerroandGatostructs that embed theAnimalstruct. Display the data of a dog and a cat. - Create a
CuentaBancariastruct with fields likesaldoandnombreTitular. Define methods to deposit and withdraw money from the account. Run a few transactions and display the final balance. - Define an
EquipoDeFutbolstruct that holds a slice of player names. Add and remove players from the team and display the updated list. - Define an
Edificiostruct that holds information about apartments, where each apartment has a number and an area in square meters. Create a method onEdificioto compute the total area of all the apartments.
That's all. I hope this post is useful to you and that you can apply it to a project you have in mind.
Leave me a comment if it helped, if you want to add an opinion, or if you have any questions. And remember, if you liked it, you can also share it using the social links below. Good luck!
Sebastian Gomez
Creador de contenido principalmente acerca de tecnología.