Structs

Creating

// Since it is not Uppercase, it is only available inside the package
type user struct {
    name string
    email string
}

Nesting structs

type system struct {
    date string
    user user
}

Struct tags

You can assign metadata to struct fields.

Some Go package functions make use of them.

For instance, the json Go package uses metadata to define the key property names. Without the metadata the json keys would be Date: "", User: "", and with them date: "", user_data: "".

type system struct {
    Date string `json:"date"`
    User user `json:"user_data"`
}

Assiging values

var user user // Will by default hold the value "user{}"

var user user = user{} // Or you may explicitly set to an empty struct

Struct Literal or Composite Literal

user = user{
    name: "Username",
    email: "user@user"
}

Or you assign pass variable values.

userName := "Username"
userEmail := "user@user"

user = user{
    name: userName,
    email: userEmail
}

If the values are passed in the same order as they were declared in the structure you may omit the keys. (Otherwise values will be assigned to the wrong keys)

user = user{
    "Username",
    "user@user"
}

Access property values

Use . to access a struct property value.

fmt.Print(user.name)

Structs as parameters

// user if passed by value
func name(user user) {
    user.name
}
name(user)

// user receives struct memory address
name(&user)
func name(user *user){
    // This is technically the correct way
    (*user).name
    
    // But Go handles accessing the normal way
    user.name
}

Struct Methods

You can attach functions as struct properties, to give behavior to structures variables.

The function is not declared inside the structure, like it is in a JS class.

Instead it is normally declared in the .go file, along with the structure, but you provide a special parameter called Receiver which states that the function belongs to the structure and allows the function to access the structure properties.

type user struct {
    name string
    age int
}

func (user user) showAge() {
    fmt.Print(user.age)
}

func main() {
    var u user = user{
        "Username",
        15
    }
    
    u.showAge()
}

Mutating struct values

To mutate and change the data you have to pass the Receiver as a pointer.

Just like regular function parameters, the Receiver parameter is also by default passed by value.

type user struct {
    name string
    age int
}

func (user *user) setAge(value int) {
    user.age = value
}

func main() {
    var u user = user{
        "Username"
    }
    
    u.setAge(15)
}

constructors

These are convention functions to handle the creation of "new instances" of struct variables.

They are not Go features, but only a convention pattern.

type user struct {
    name string
    age int
}

func newUser(name string, age int) *user {
    return &user{
        name,
        age
    }
}

var user *user = newUser('Username', 15)

Isolating them in packages

user.go
package user

type User struct {
    // Don't do it like this, check bellow
    Name string
    Age int
}

func New(name string, age int) *user {
    return &user{
        name,
        age
    }
}
main.go
package main

import "your-module/user"

var user *user.User = user.newUser("Username", 15)

Following object oriented principles

user.go
package user

type User struct {
    name string
    age int
}

func (user user) PrintAge() {
    fmt.Print(user.age)
}

func New(name string, age int) (*user, error) {
    if name == "" || age == 0 {
        return nil, errors.New("invalid values for User")
    }
    return &user{
        name,
        age
    }, nil
}

The constructor should be available, or even other types of design patterns, to preserve entity integrity.

This way you may even set validation on struct creation.

Struct embedding

Also known as inheritance in other Object Oriented languages.

Define anonymously

user.go
package user

type User struct {
    name string
    age int
}

type Admin struct {
    email string
    password string
    // Here "Admin" will inherit "User" anonymously, but privately
    User
    // To inherit anonymously and public
    User: User
}

...

func NewAdmin() Admin {
    return Admin{
        email: "",
        password: "",
        User: User{
            name: "Admin",
            age: 45
        }
    }
}
admin := user.NewAdmin()

// Access to the "User" structure will show normaly
admin.PrintAge()

Define with a specific name

user.go
package user

type User struct {
    name string
    age int
}

type Admin struct {
    email string
    password string
    // Here "Admin" will inherit "User" with by a specific "user" name
    User User
}

...

func NewAdmin() Admin {
    return Admin{
        email: "",
        password: "",
        // Must use the specified name
        User: User{
            name: "Admin",
            age: 45
        }
    }
}
admin := user.NewAdmin()

// Access to the "User" structure must "go through the "user" property
admin.User.PrintAge()

Last updated