138 lines
3.5 KiB
Markdown
138 lines
3.5 KiB
Markdown
---
|
|
title: Go Structs
|
|
---
|
|
## Go Structs
|
|
|
|
In Go, structs are used to store data and related functions. An example might be a struct to represent a User:
|
|
|
|
```go
|
|
type User struct {
|
|
FirstName string
|
|
LastName string
|
|
Email string
|
|
Age int
|
|
}
|
|
```
|
|
|
|
Here we can store a user's first name, last name, email address, and age. The name of the property is followed by the type of data we want to store. For example, the `FirstName` property is a `string` whereas the `Age` property is an `int`.
|
|
|
|
### Creating objects
|
|
|
|
To initialise a new object, we can use the Go shorthand syntax for creating and assigning variables. We can either pass the data in at this point or set the data at a later time:
|
|
|
|
```go
|
|
func main() {
|
|
type MyInt int64
|
|
|
|
// Create a user and set both the first and last name properties
|
|
user1 := User{
|
|
FirstName: "John",
|
|
LastName: "Wick",
|
|
}
|
|
|
|
// Now we have our user object, we can set the data like this
|
|
user1.Email = "john@wick.com"
|
|
user1.Age = 30
|
|
}
|
|
```
|
|
|
|
### Object methods
|
|
|
|
Go enables declaring methods to struct types and non struct types. This enables grouping of relevant operations to the data it affects. In this example we will write a method on the `User` struct to generate the full name of the user and String method on `MyInt` type to return a String:
|
|
|
|
```go
|
|
func (myint MyInt) String() string {
|
|
return fmt.Sprintf("%d", myint)
|
|
}
|
|
|
|
func (u User) FullName() string {
|
|
return strings.Join([]string{u.FirstName, u.LastName}, " ")
|
|
}
|
|
```
|
|
|
|
This method will join the first and last name of the user with a space in between. Calling the method might look like this:
|
|
|
|
```go
|
|
fmt.println(user1.FullName())
|
|
```
|
|
|
|
### Struct Tags
|
|
|
|
Struct tags are used to modify how encoders handle the data. For example, setting the key names when encoding to JSON:
|
|
|
|
```go
|
|
type User struct {
|
|
FirstName string `json:"first_name"`
|
|
LastName string `json:"last_name"`
|
|
Email string `json:"email"`
|
|
Age int `json:"age"`
|
|
}
|
|
```
|
|
|
|
### Exported Data
|
|
|
|
Structs can contain both exported (public) and unexported (private) properties. This is set by either having an uppercase first letter for exported or a lowercase first letter for unexported. In this example, we will make the email property private:
|
|
|
|
```go
|
|
type User struct {
|
|
// Exported Data
|
|
FirstName string
|
|
LastName string
|
|
Age int
|
|
|
|
// Unexported Data
|
|
email string
|
|
}
|
|
```
|
|
|
|
Doing this will make the following code throw a compilation error as it is trying to assign value to an unexported property:
|
|
|
|
```go
|
|
user1.email = "john@wick.com"
|
|
```
|
|
|
|
Same principle applies when attempting to read data from an unexported property.
|
|
|
|
This also applies to methods:
|
|
|
|
```go
|
|
// Exported method. This can be called from anywhere
|
|
func (u User) Email() {
|
|
return u.email
|
|
}
|
|
|
|
// Unexported method. This can only be called by other methods on this struct
|
|
func (u User) updateLoginCount {
|
|
// code to update login count...
|
|
}
|
|
```
|
|
|
|
### Modifying properties via methods
|
|
|
|
To modify the data of an object from within one of its methods, the object must be a pointer. An example might look like this:
|
|
|
|
```go
|
|
// SetEmail sets the user's email address
|
|
func (u *User) SetEmail(email string) {
|
|
u.email = email
|
|
}
|
|
|
|
// Email accessor
|
|
func (u *User) Email() string {
|
|
return u.email
|
|
}
|
|
|
|
func main() {
|
|
// Creating the user1 pointer
|
|
user1 = &User{
|
|
FirstName: "John",
|
|
LastName: "Wick",
|
|
}
|
|
|
|
// Set the user's email address
|
|
user1.SetEmail("john@wick.com")
|
|
|
|
// Access and print the user's email address
|
|
fmt.println(user1.Email())
|
|
}
|