freeCodeCamp/guide/english/go/go-structs/index.md

3.5 KiB

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:

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:

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:

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:

    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:

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:

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:

    user1.email = "john@wick.com"

Same principle applies when attempting to read data from an unexported property.

This also applies to methods:

// 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:

// 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())
}