Go struct
last modified April 11, 2024
In this article we show how to work with structures in Golang.
$ go version go version go1.22.2 linux/amd64
We use Go version 1.22.2.
The struct
A struct is a user-defined type that contains a collection of fields. It is used to group related data to form a single unit. A Go struct can be compared to a lightweight class without the inheritance feature.
Go struct definition
A struct is defined with the type
keyword.
type User struct { name string occupation string age int }
A new type is created with the type
keyword. It is followed
by the name of the type (User). The struct
keyword indicates
that we are creating a struct. Inside the curly brackets, we have a list of
fields. Each field has a name and a type.
Go initialize struct
We show how to initialize struct types in Go.
u := User{"John Doe", "gardener", 34}
A new User struct is created. The struct fields are initialized with the values provided between the curly brackets. In this case, the order of the fields is relevant.
u := User{ name: "John Doe", occupation: "gardener", age: 34, }
We can provide both the field names and values. In this case, the order is not important. Note that the last comma is mandatory.
u := User{}
If we omit the values in the curly brackets, they are initialized to zero values.
Go struct simple example
The following is a Go struct simple example.
package main import "fmt" type User struct { name string occupation string age int } func main() { u := User{"John Doe", "gardener", 34} fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation) }
We define a User
struct with three fields.
type User struct { name string occupation string age int }
We declare the User
struct.
u := User{"John Doe", "gardener", 34}
We initialize the User
struct.
fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation)
We print the contents of the User
struct.
$ go run main.go John Doe is 34 years old and he is a gardener
Go struct access fields
The struct fields are accessed with the dot operator.
package main import "fmt" type User struct { name string occupation string age int } func main() { u := User{} u.name = "John Doe" u.occupation = "gardener" u.age = 34 fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation) }
We create an empty User
struct. We initialize the fields with
values and read them using the dot operator.
Go anonymous struct
It is possible to create anonymous structs in Go. Anonymous structs do not have a name. They are created only once.
package main import "fmt" func main() { u := struct { name string occupation string age int }{ name: "John Doe", occupation: "gardener", age: 34, } fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation) }
An anonymous struct is created only with the struct
keyword. The
declaration of the struct is followed by its initialization.
Go nested structs
Go structs can be nested.
package main import "fmt" type Address struct { city string country string } type User struct { name string age int address Address } func main() { p := User{ name: "John Doe", age: 34, address: Address{ city: "New York", country: "USA", }, } fmt.Println("Name:", p.name) fmt.Println("Age:", p.age) fmt.Println("City:", p.address.city) fmt.Println("Country:", p.address.country) }
In the code example, the Address
struct is nested inside the
User
struct.
fmt.Println("City:", p.address.city) fmt.Println("Country:", p.address.country)
To access the fields of the nested struct, we access first the inner struct with the dot operator; then we access the respective fields.
$ go run nested.go Name: John Doe Age: 34 City: New York Country: USA
Go struct promoted fields
The fields of a nested anonymous struct are promoted; that is, they are accessed without referring to the nested struct.
package main import "fmt" type Address struct { city string country string } type User struct { name string age int Address } func main() { p := User{ name: "John Doe", age: 34, Address: Address{ city: "New York", country: "USA", }, } fmt.Println("Name:", p.name) fmt.Println("Age:", p.age) fmt.Println("City:", p.city) fmt.Println("Country:", p.country) }
In the code example, we have a nested Address
struct.
type User struct { name string age int Address }
The User
struct has a nested anonymous Address
struct.
The field does not have a name.
fmt.Println("City:", p.city) fmt.Println("Country:", p.country)
The city
and country
fields are promoted. They are
accessed by directly referring to the parent struct.
$ go run main.go Name: John Doe Age: 34 City: New York Country: USA
Go struct functions fields
The struct fields can be functions.
package main import "fmt" type Info func(string, string, int) string type User struct { name string occupation string age int info Info } func main() { u := User{ name: "John Doe", occupation: "gardener", age: 34, info: func(name string, occupation string, age int) string { return fmt.Sprintf("%s is %d years old and he is a %s\n", name, age, occupation) }, } fmt.Printf(u.info(u.name, u.occupation, u.age)) }
In the code example, we have the User
struct. Its info
field is a function called Info
.
Go struct pointer
A pointer to the struct can be created with the &
operator or
the new
keyword. A pointer is dereferenced with the *
operator.
package main import "fmt" type Point struct { x int y int } func main() { p := Point{3, 4} p_p := &p (*p_p).x = 1 p_p.y = 2 fmt.Println(p) }
In the code example, we create a pointer to the Point
struct.
p_p := &p
The &
operator returns a pointer to the Point
structure.
(*p_p).x = 1 p_p.y = 2
A pointer is dereferenced with the *
operator. Go also allows to
use the dot operator directly.
Alternatively, we can create a pointer to a struct with the new
keyword.
package main import "fmt" type User struct { name string occupation string age int } func main() { u := new(User) u.name = "Richard Roe" u.occupation = "driver" u.age = 44 fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation) }
The example creates a new pointer to the User
struct with the
new
keyword.
Go struct constructor
There are no built-in constructors in Go. Programmers sometimes create constructor functions as a best practice.
package main import "fmt" type User struct { name string occupation string age int } func newUser(name string, occupation string, age int) *User { p := User{name, occupation, age} return &p } func main() { u := newUser("Richard Roe", "driver", 44) fmt.Printf("%s is %d years old and he is a %s\n", u.name, u.age, u.occupation) }
In the code example, we have the newUser
constructor function, which
creates new User
structs. The function returns a pointer to the
newly created struct.
Go struct is a value type
Go structs are value types. When we assign a struct variable to another struct variable, a new copy of the struct is created. Likewise, when we pass a struct to another function, the function receives a new copy of the struct.
package main import "fmt" type User struct { name string occupation string age int } func main() { u1 := User{"John Doe", "gardener", 34} u2 := u1 u2.name = "Richard Roe" u2.occupation = "driver" u2.age = 44 fmt.Printf("%s is %d years old and he is a %s\n", u1.name, u1.age, u1.occupation) fmt.Printf("%s is %d years old and he is a %s\n", u2.name, u2.age, u2.occupation) }
In the code example, we assign a struct to another struct. Changing the fields of the new struct does not affect the original struct.
$ go run main.go John Doe is 34 years old and he is a gardener Richard Roe is 44 years old and he is a driver
The two structs are distinct entities.
Comparing Go structs
Go structs are equal if all their corresponding fields are equal.
package main import "fmt" type Point struct { x int y int } func main() { p1 := Point{3, 4} p2 := Point{3, 4} if p1 == p2 { fmt.Println("The structs are equal") } else { fmt.Println("The structs are not equal") } }
In the code example, we compare two Point
structs.
$ go run main.go The structs are equal
Go export struct
Named structs that start with a capital letter are exported and accessible from outside their packages. Similarly, struct fields that start with a capital letter are exported. Struct names and fields starting with a small letter are visible only inside their package.
$ go mod init exporting
We create a new Go module with the go mod init
command.
go.mod main └── main.go model ├── address.go └── user.go
This is the project structure.
package model type User struct { Name string Occupation string age int }
The Name
and Occupation
fields of the User
struct are exported, while the age
field is not.
package model type address struct { city string country string }
The address
struct is not exported. We cannot refer to it in the
main.go
file.
package main import ( "exporting/model" "fmt" ) func main() { u := model.User{Name: "John Doe", Occupation: "gardener"} fmt.Printf("%s is a %s\n", u.Name, u.Occupation) }
In the main.go
file, we import the Name
and Occupation
fields from the exporting/model
package.
Go create a slice of structs
In the following example, we create a slice of structs.
package main import "fmt" type User struct { name string occupation string country string } func main() { users := []User{} users = append(users, User{"John Doe", "gardener", "USA"}) users = append(users, User{"Roger Roe", "driver", "UK"}) users = append(users, User{"Paul Smith", "programmer", "Canada"}) users = append(users, User{"Lucia Mala", "teacher", "Slovakia"}) users = append(users, User{"Patrick Connor", "shopkeeper", "USA"}) users = append(users, User{"Tim Welson", "programmer", "Canada"}) users = append(users, User{"Tomas Smutny", "programmer", "Slovakia"}) for _, user := range users { fmt.Println(user) } }
A User
type is defined. Then we create an empty slice of User
structs. We add elements to the slice with append
.
Go filter slice of structs
In the next example, we filter a slice of Go structures.
package main import "fmt" type User struct { name string occupation string country string } func main() { users := []User{ {"John Doe", "gardener", "USA"}, {"Roger Roe", "driver", "UK"}, {"Paul Smith", "programmer", "Canada"}, {"Lucia Mala", "teacher", "Slovakia"}, {"Patrick Connor", "shopkeeper", "USA"}, {"Tim Welson", "programmer", "Canada"}, {"Tomas Smutny", "programmer", "Slovakia"}, } var programmers []User for _, user := range users { if isProgrammer(user) { programmers = append(programmers, user) } } fmt.Println("Programmers:") for _, u := range programmers { fmt.Println(u) } } func isProgrammer(user User) bool { return user.occupation == "programmer" }
In the code example, we define a slice of users. We create a new slice that contains only programmers.
type User struct { name string occupation string country string }
The User
struct has three fields.
users := []User{ {"John Doe", "gardener", "USA"}, {"Roger Roe", "driver", "UK"}, {"Paul Smith", "programmer", "Canada"}, {"Lucia Mala", "teacher", "Slovakia"}, {"Patrick Connor", "shopkeeper", "USA"}, {"Tim Welson", "programmer", "Canada"}, {"Tomas Smutny", "programmer", "Slovakia"}, }
This is the original slice of User
structures.
var programmers []User
The filtered users/programmers are stored in the programmers
slice.
for _, user := range users { if isProgrammer(user) { programmers = append(programmers, user) } }
We go over the users
slice and add a current user to the
programmers
slice only if the user satisfies the
isProgrammer
predicate.
func isProgrammer(user User) bool { return user.occupation == "programmer" }
The IsProgrammer
predicate returns true for all users whose
occupation
field equals to "programmer".
$ go run main.go Programmers: {Paul Smith programmer Canada} {Tim Welson programmer Canada} {Tomas Smutny programmer Slovakia}
Source
The Go Programming Language Specification
In this article we have covered structs in Golang.
Author
List all Go tutorials.