kdocs
GitHub
Lang - Runtime
Lang - Runtime
  • Go
    • Modules & Packages
    • Variables
    • Structs
    • Interfaces
    • Functions
    • Control Structures
    • Erro Handling
    • Concurrency
    • Testing
    • Fuzzing
  • Web
    • V8 Engine
    • Node.js
      • New Project
    • Deno
      • New Project
Powered by GitBook
On this page
  • Creating
  • Assiging values
  • Struct Literal or Composite Literal
  • Access property values
  • Structs as parameters
  • Struct Methods
  • constructors
  • Isolating them in packages
  • Struct embedding
  1. Go

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

When unassigned a value, a struct variable will be structname{}.

For instance, the user variable "null" value will be the empty struct user{}.

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"
}

Unassigned struct properties will have their default "null" values.

Access property values

Use . to access a struct property value.

fmt.Print(user.name)

Structs as parameters

Structs are good examples of when to use pointers to pass data by reference, in order to avoid memory duplication.

If passed without pointers, they will be passed by value.

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

As stated above in the comments, Go handles as a shortcut, to access properties of a structure pointer the same way as a structure value. (With .)

There is no need to access the pointer value, before accessing the property value, like (*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

The way done above can only read values from the struct.

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.

By convention, constructors start with new prefix in the function name.

type user struct {
    name string
    age int
}

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

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

Also make sure to return references to the data, otherwise two copies will be created.

  • One inside the function.

  • One for the outside.

Isolating them in packages

In a struct, not only its name should be Uppercase, but also all the properties that should be "public".

When isolated, it is common that the constructor function will be named only New.

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

Since properties of a "class" should not be available to be used at will, struct properties should also no be accessible.

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

PreviousVariablesNextInterfaces

Last updated 1 month ago