Getting into Golang 1.18

Go 1.18

New features of Golang 1.18 with tutorial and examples. In this repository I introduce new features of Golang version 1.18 with their examples and resources.

Contents

Introduction

The latest Go release, version 1.18, is a significant release, including changes to the language, implementation of the toolchain, runtime, and libraries. Go 1.18 arrives seven months after Go 1.17. As always, the release maintains the Go 1 promise of compatibility. We expect almost all Go programs to continue to compile and run as before.

Getting started with generics

To support values of either type, that single function will need a way to declare what types it supports. To support this, you’ll write a function that declares type parameters in addition to its ordinary function parameters. These type parameters make the function generic, enabling it to work with arguments of different types. You’ll call the function with type arguments and ordinary function arguments. While a type parameter’s constraint typically represents a set of types, at compile time the type parameter stands for a single type – the type provided as a type argument by the calling code. If the type argument’s type isn’t allowed by the type parameter’s constraint, the code won’t compile.

Example:

// SumIntsOrFloats sums the values of map m. It supports both int64 and float64
// as types for map values.
func SumIntsOrFloats[K comparable, V int64 | float64](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}
SumIntsOrFloats[string, int64](ints)
SumIntsOrFloats[string, float64](floats))

You can omit type arguments in calling code when the Go compiler can infer the types you want to use. The compiler infers type arguments from the types of function arguments.

Note that this isn’t always possible. For example, if you needed to call a generic function that had no arguments, you would need to include the type arguments in the function call.

SumIntsOrFloats(ints)
SumIntsOrFloats(floats))

Declare a type constraint

You declare a type constraint as an interface. The constraint allows any type implementing the interface. For example, if you declare a type constraint interface with three methods, then use it with a type parameter in a generic function, type arguments used to call the function must have all of those methods.

type Number interface {
    int64 | float64
}

func SumNumbers[K comparable, V Number](m map[K]V) V {
    var s V
    for _, v := range m {
        s += v
    }
    return s
}

See the source code of the example.

Getting started with fuzzing

Fuzzing, sometimes also called fuzz testing, is the practice of giving unexpected input to your software. Ideally, this test causes your application to crash, or behave in unexpected ways. Regardless of what happens, you can learn a lot from how your code reacts to data it wasn't programmed to accept, and you can add appropriate error handling.

Fuzzing is a technique where you automagically generate input values for your functions to find bugs. The unit test has limitations, namely that each input must be added to the test by the developer. One benefit of fuzzing is that it comes up with inputs for your code, and may identify edge cases that the test cases you came up with didn’t reach.

General:

func FuzzXXX(f *testing.F) {
    f.Add() // Adding your own inputs if you want
    f.Fuzz(func(t *testing.T) {
        // Write your tests
    })
}

Now run your tests:

go test -fuzz=Fuzz

Example:

func FuzzReverse(f *testing.F) {
    testcases := []string{"Hello, world", " ", "!12345"}
    for _, tc := range testcases {
        f.Add(tc)  // Use f.Add to provide a seed corpus
    }

    f.Fuzz(func(t *testing.T, orig string) {
        rev := Reverse(orig)
        doubleRev := Reverse(rev)
        if orig != doubleRev {
            t.Errorf("Before: %q, after: %q", orig, doubleRev)
        }
        if utf8.ValidString(orig) && !utf8.ValidString(rev) {
            t.Errorf("Reverse produced invalid UTF-8 string %q", rev)
        }
    })
}

When fuzzing, you can’t predict the expected output, since you don’t have control over the inputs.
Note the syntax differences between the unit test and the fuzz test:

  • The function begins with FuzzXxx instead of TestXxx, and takes *testing.F instead of *testing.T
  • Where you would expect to a see a t.Run execution, you instead see f.Fuzz which takes a fuzz target function whose parameters are *testing.T and the types to be fuzzed. The inputs from your unit test are provided as seed corpus inputs using f.Add.

See the source code of the example.

Getting started with multi-module workspaces

A workspace is a collection of modules on disk that are used as the root modules when running minimal version selection (MVS).

A workspace can be declared in a go.work file that specifies relative paths to the module directories of each of the modules in the workspace. When no go.work file exists, the workspace consists of the single module containing the current directory.

Most go subcommands that work with modules operate on the set of modules determined by the current workspace. go mod init, go mod why, go mod edit, go mod tidy, go mod vendor, and go get always operate on a single main module.

With multi-module workspaces, you can tell the Go command that you’re writing code in multiple modules at the same time and easily build and run code in those modules.

Initialize the workspace:

go work init [Directory name]

The go.work file has similar syntax to go.mod, the go directive tells Go which version of Go the file should be interpreted with. It’s similar to the go directive in the go.mod file. The use directive tells Go that the module in the given directory should be main modules when doing a build.

The go work init command tells go to create a go.work file for a workspace containing the modules in the given directory.
The go command produces a go.work file that looks like this:

go 1.18

use (
    ./[Workspace/directory name]
)

Creating workspaces:

go work init ./mod ./tools

The output project structur will be:

Project
├── mod
│   ├── go.mod      
│   └── main.go
├── go.work         
└── tools
    ├── fish.go
    └── go.mod      

The content of go.work file:

go 1.18

use (
    ./mod 
    ./tools
)

A total of three directives are supported within the go.work file:

  • go: declares the go version number, mainly for subsequent version control of new semantics.
  • use: declares the specific file path of a module on which the application depends. The path can be either absolute or relative, and can be outside the application’s destiny directory.
  • replace: Declares that the import path of a module dependency is replaced, with priority over the replace directive in go.mod.

Example of go.work file with directives:

go 1.18

use (
    ./baz // foo.org/bar/baz
    ./tools // golang.org/x/tools
)

replace golang.org/x/net => example.com/fork/net v1.4.5

The go command has a couple of subcommands for working with workspaces in addition to go work init:

  • go work use [-r] [dir] adds a use directive to the go.work file for dir, if it exists, and removes the use directory if the argument directory doesn’t exist. The -r flag examines subdirectories of dir recursively.
  • go work edit edits the go.work file similarly to go mod edit
  • go work sync syncs dependencies from the workspace’s build list into each of the workspace modules.

The go.work file doesn’t need to be committed to a Git repository, otherwise it’s a bit of a toss-up. As long as you have go.work set up in your Go project, you will be in workspace mode at runtime and compile time, and the workspace configuration will be given highest priority to suit your local development needs.

If you want to disable workspace mode, you can specify it with the -workfile=off command.

That is, execute the following command at runtime.

go run -workfile=off main.go

go build -workfile=off

20% Performance Improvements

Apple M1, ARM64, and PowerPC64 users rejoice! Go 1.18 includes CPU performance improvements of up to 20% due to the expansion of Go 1.17’s register ABI calling convention to these architectures. Just to underscore how big this release is, a 20% performance improvement is the fourth most important headline!

Examples

In this section I implemented some data structures with Golang generics.

Stack

Defining the data types:

type Data interface {
	int64 | float64 | string
}

type Node[T Data] struct {
	Next  *Node[T]
	Value T
}

type Stack[T Data] struct {
	Head *Node[T]
}

Now we can create our stack in any type we want:

s := Stack[int64]{}
s.Push(12)
s.Push(129)
s.Push(160)
temp := s.Pop()

See the source code of the example.

Linked List

Same types, but different methods:

type Data interface {
	int64 | float64 | string
}

type Node[T Data] struct {
	Next  *Node[T]
	Value T
}

type LinkedList[T Data] struct {
	Head *Node[T]
}

Now we can build our linked list on any type:

l := LinkedList[float64]{}
l.Add(12.1)
l.Add(22.45)
l.Add(0.75)
l.Iterate()
l.Remove(12.1)

See the source code of the example.

Binary Tree

Type parameter and data structs:

type Data interface {
	int | int32 | int64 | float64
}

type Node[T Data] struct {
	Parent *Node[T]
	Left   *Node[T]
	Right  *Node[T]
	key    T
}

type Tree[T Data] struct {
	Root *Node[T]
}

Now we can create our tree with each type we want:

tree := Tree[int]{}
tree.Insert(20)
tree.Insert(25)
tree.Insert(24)
tree.Delete(20)

See the source code of the example.

Resources

Examples and Tutorials

Owner
AmirH.Najafizadeh
Amirhossein Najafizadeh, 20 years old computer engineer/backend developer.
AmirH.Najafizadeh
Similar Resources

Tiny Go tool for running multiple functions concurrently and collecting their results into an error slice.

Overview Short for "ConCurrent". Tiny Go tool for running multiple functions concurrently and collecting their results into an error slice. Dependency

Nov 22, 2021

Small tool for splitting files found in a path into multiple groups

Small tool for splitting files found in a path into multiple groups. Usefull for parallelisation of whatever can be paralleled with multiple files.

Jan 30, 2022

Goridge is high performance PHP-to-Golang codec library which works over native PHP sockets and Golang net/rpc package.

Goridge is high performance PHP-to-Golang codec library which works over native PHP sockets and Golang net/rpc package.

Goridge is high performance PHP-to-Golang codec library which works over native PHP sockets and Golang net/rpc package. The library allows you to call Go service methods from PHP with a minimal footprint, structures and []byte support.

Dec 28, 2022

memresolver is an in-memory golang resolver that allows to override current golang Lookup func literals

mem-resolver memresolver is an in-memory golang resolver that allows to override current golang Lookup func literals How to use it Create your custom

Jun 23, 2022

Govalid is a data validation library that can validate most data types supported by golang

Govalid is a data validation library that can validate most data types supported by golang. Custom validators can be used where the supplied ones are not enough.

Apr 22, 2022

Code Generation for Functional Programming, Concurrency and Generics in Golang

goderive goderive derives mundane golang functions that you do not want to maintain and keeps them up to date. It does this by parsing your go code fo

Dec 25, 2022

Golang source code parsing, usage like reflect package

gotype Golang source code parsing, usage like reflect package English 简体中文 Usage API Documentation Examples License Pouch is licensed under the MIT Li

Dec 9, 2022

Golang library to act on structure fields at runtime. Similar to Python getattr(), setattr(), hasattr() APIs.

go-attr Golang library to act on structure fields at runtime. Similar to Python getattr(), setattr(), hasattr() APIs. This package provides user frien

Dec 16, 2022

Composable HTML components in Golang

Composable HTML components in Golang

daz Composable HTML components in Golang Daz is a "functional" alternative to using templates, and allows for nested components/lists Also enables tem

Oct 3, 2022
Related tags
404 Page

404

Sorry! Page not found.

Unfortunately the page you are looking for has been moved or deleted.