Table of Contents
Immutability in Golang" >Immutability in Golang
Only Export the functionality of a struct without exporting its fields" >Only Export the functionality of a struct without exporting its fields
Use value copy instead of pointer in function" >Use value copy instead of pointer in function
减少全局或外部状态中的依赖性" >减少全局或外部状态中的依赖性
Home Backend Development Golang Detailed explanation of immutable types in Go

Detailed explanation of immutable types in Go

Jun 15, 2020 pm 06:01 PM
go golang

Detailed explanation of immutable types in Go

Immutability in Golang

How to leverage immutability to enhance the readability and stability of your Golang applications

The concept of immutability is very simple. Once an object (or struct) is created, it can never be changed. It is immutable. Although the concept seems simple, using it or benefiting from it is not so Easy.

As with most things in computer science (and life), there are many ways to achieve the same result, and in terms of invariance there is no difference. You should think of it as is a tool in the toolkit and is used on applicable problem scenarios. A very good use case for immutability is when you are doing concurrent programming. Golang was designed with concurrency in mind, so using concurrency in go Very common.

No matter which paradigm you use, here are some ways to use some immutability concepts in Golang to make your code more readable and stable.

Only Export the functionality of a struct without exporting its fields

This is similar to encapsulation. Create a struct with non-exported fields and export only the functions that act. Since you are only interested in the behavior of those structures, This technique is very useful for interfaces. Another good addition to this technique is to add and export a creation function (or constructor) to your structure. This way you can ensure that the state of the structure is always valid. always remains valid Can make the code more reliable because you don't have to keep dealing with invalid state for every operation you want to do with the structure. Here is a very basic example:

package amounts

import "errors"

type Amount struct {
    value int
}

func NewAmount(value int) (Amount, error) {
    if value < 0 {
        return Amount{}, errors.New("Invalid amount")
    }

    return Amount{value: value}, nil
}

func (a Amount) GetValue() int {
    return a.value
}
Copy after login

In this package, we define Amount type, has unexported field value, constructor NewAmount and GetValue method for Amount type. Once The NewAmount function creates the Amount structure, which cannot be changed. Therefore it is immutable from outside the package (although there are suggestions to change this in go 2, but There is no way to create immutable structures in go 1). Furthermore there are no variables of type Amount that are in an invalid state (negative in this case), since the only way to create them already verifies this . We can call it from another package:

a, err := amounts.NewAmount(10)
*// 处理错误
*log.Println(a.GetValue())
Copy after login

Use value copy instead of pointer in function

The most basic concept is to create an object (or structure body) and never change it again. But we often work on applications where entity state is important. However, the entity state and the entity's internal representation in the program are different. When using immutability, we can still assign multiple states to entities. This means that the created structure will not change, but its copy will. This does not mean that we need to manually implement the function of copying each field in the structure.

Instead, we can rely on the Go language's native behavior of copying values ​​when calling functions. For any operation that changes the state of an entity, we can create a function that receives a structure as a parameter (or as a function receiver) and returns the changed version after execution. This is a very powerful technique because you are able to change anything on the copy without changing the variables passed as arguments by the function caller. This means no side effects and predictable behavior. If the same structure is passed to concurrent functions, each structure receives a copy of it rather than a pointer to it.

When you are using the slicing function, you will see this behavior applied to [append](https://golang.org/pkg/builtin/#append) Function

Back to our example, let's implement the Account type, which contains the
balance field of type Amount. At the same time, we add Deposit and Withdraw methods to change the state of the Account entity.

package accounts

import (
    "errors"
    "my-package/amounts"
)

type Account struct {
    balance amounts.Amount
}

func NewEmptyAccount() Account {
    amount, _ := amounts.NewAmount(0)
    return NewAccount(amount)
}

func NewAccount(amount amounts.Amount) Account {
    return Account{balance: amount}
}

func (acc Account) Deposit(amount amounts.Amount) Account {
    newAmount, _ := amounts.NewAmount(acc.balance.GetValue() + amount.GetValue())
    acc.balance = newAmount
    return acc
}

func (acc Account) Withdraw(amount amounts.Amount) (Account, error) {
    newAmount, err := amounts.NewAmount(acc.balance.GetValue() - amount.GetValue())
    if err != nil {
        return acc, errors.New("Insuficient funds")
    }
    acc.balance = newAmount
    return acc, nil
}
Copy after login

If you inspect the methods we created, they will appear that we are actually changing the state of the Account structure that is the receiver of the function. Since we are not using pointers, this is not the case, and since a copy of the struct is passed as the receiver of these functions, we will change the copy that is only valid within the function scope and then return it. Here's an example of calling it in another package:

a, err := amounts.NewAmount(10)
acc := accounts.NewEmptyAccount()
acc2 := acc.Deposit(a)
log.Println(acc.GetBalance())
log.Println(acc2.GetBalance())
Copy after login

The result on the command line would be like this:

2020/06/03 22:22:40 {0}
2020/06/03 22:22:40 {10}
Copy after login

As you can see, despite passing the variable acc The Deposit method is called, but the variable does not actually change. It returns a new copy of Account (assigned to acc2), which contains the changed field.

使用指针具有优于复制值的优点,特别是如果您的结构很大时,在复制时可能会导致性能问题,但是您应始终问自己是否值得,不要尝试过早地优化代码。尤其是在使用并发时。您可能会在一些糟糕的情况下结束。

减少全局或外部状态中的依赖性

不变性不仅可以应用于结构,还可以应用于函数。如果我们用相同的参数两次执行相同的函数,我们应该收到相同的结果,对吗?好吧,如果我们依赖于外部状态或全局变量,则可能并非总是如此。最好避免这种情况。有几种方法可以实现这一目标。

如果您在函数内部使用共享的全局变量,请考虑将该值作为参数传递,而不是直接在函数内部使用。 那会使您的函数更可预测,也更易于测试。整个代码的可读性也会更容易,其他人也将会了解到值可能会影响函数行为,因为它是一个参数,而这就是参数的用途。 这里有一个例子:

package main

import (
    "fmt"
    "time"
)

var rand int = 0

func main() {
    rand = time.Now().Second() + 1
    fmt.Println(sum(1, 2))
}

func sum(a, b int) int {
    return a + b + rand
}
Copy after login

这个函数 sum 使用全局变量作为自己计算的一部分。 从函数签名来看这不是很清楚。 更好的方法是将rand变量作为参数传递。 因此该函数看起来应该像这样:

func sum(a, b, rand **int**) **int** {
   return a + b + rand
}
Copy after login

  推荐教程:《Go教程

The above is the detailed content of Detailed explanation of immutable types in Go. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undress AI Tool

Undress AI Tool

Undress images for free

Clothoff.io

Clothoff.io

AI clothes remover

AI Hentai Generator

AI Hentai Generator

Generate AI Hentai for free.

Hot Article

R.E.P.O. Energy Crystals Explained and What They Do (Yellow Crystal)
2 weeks ago By 尊渡假赌尊渡假赌尊渡假赌
Hello Kitty Island Adventure: How To Get Giant Seeds
1 months ago By 尊渡假赌尊渡假赌尊渡假赌
Two Point Museum: All Exhibits And Where To Find Them
1 months ago By 尊渡假赌尊渡假赌尊渡假赌

Hot Tools

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How to safely read and write files using Golang? How to safely read and write files using Golang? Jun 06, 2024 pm 05:14 PM

Reading and writing files safely in Go is crucial. Guidelines include: Checking file permissions Closing files using defer Validating file paths Using context timeouts Following these guidelines ensures the security of your data and the robustness of your application.

How to configure connection pool for Golang database connection? How to configure connection pool for Golang database connection? Jun 06, 2024 am 11:21 AM

How to configure connection pooling for Go database connections? Use the DB type in the database/sql package to create a database connection; set MaxOpenConns to control the maximum number of concurrent connections; set MaxIdleConns to set the maximum number of idle connections; set ConnMaxLifetime to control the maximum life cycle of the connection.

Comparison of advantages and disadvantages of golang framework Comparison of advantages and disadvantages of golang framework Jun 05, 2024 pm 09:32 PM

The Go framework stands out due to its high performance and concurrency advantages, but it also has some disadvantages, such as being relatively new, having a small developer ecosystem, and lacking some features. Additionally, rapid changes and learning curves can vary from framework to framework. The Gin framework is a popular choice for building RESTful APIs due to its efficient routing, built-in JSON support, and powerful error handling.

What are the best practices for error handling in Golang framework? What are the best practices for error handling in Golang framework? Jun 05, 2024 pm 10:39 PM

Best practices: Create custom errors using well-defined error types (errors package) Provide more details Log errors appropriately Propagate errors correctly and avoid hiding or suppressing Wrap errors as needed to add context

How to use gomega for assertions in Golang unit tests? How to use gomega for assertions in Golang unit tests? Jun 05, 2024 pm 10:48 PM

How to use Gomega for assertions in Golang unit testing In Golang unit testing, Gomega is a popular and powerful assertion library that provides rich assertion methods so that developers can easily verify test results. Install Gomegagoget-ugithub.com/onsi/gomega Using Gomega for assertions Here are some common examples of using Gomega for assertions: 1. Equality assertion import "github.com/onsi/gomega" funcTest_MyFunction(t*testing.T){

How to save JSON data to database in Golang? How to save JSON data to database in Golang? Jun 06, 2024 am 11:24 AM

JSON data can be saved into a MySQL database by using the gjson library or the json.Unmarshal function. The gjson library provides convenience methods to parse JSON fields, and the json.Unmarshal function requires a target type pointer to unmarshal JSON data. Both methods require preparing SQL statements and performing insert operations to persist the data into the database.

How to solve common security problems in golang framework? How to solve common security problems in golang framework? Jun 05, 2024 pm 10:38 PM

How to address common security issues in the Go framework With the widespread adoption of the Go framework in web development, ensuring its security is crucial. The following is a practical guide to solving common security problems, with sample code: 1. SQL Injection Use prepared statements or parameterized queries to prevent SQL injection attacks. For example: constquery="SELECT*FROMusersWHEREusername=?"stmt,err:=db.Prepare(query)iferr!=nil{//Handleerror}err=stmt.QueryR

Golang framework vs. Go framework: Comparison of internal architecture and external features Golang framework vs. Go framework: Comparison of internal architecture and external features Jun 06, 2024 pm 12:37 PM

The difference between the GoLang framework and the Go framework is reflected in the internal architecture and external features. The GoLang framework is based on the Go standard library and extends its functionality, while the Go framework consists of independent libraries to achieve specific purposes. The GoLang framework is more flexible and the Go framework is easier to use. The GoLang framework has a slight advantage in performance, and the Go framework is more scalable. Case: gin-gonic (Go framework) is used to build REST API, while Echo (GoLang framework) is used to build web applications.

See all articles