Essentials
Error Handling
Go programs express error state with error
values, a built-in interface.
// https://go.dev/tour/methods/19
// the error type
type error interface {
Error() string
}
// failable & handling error
import "fmt"
i, err := strconv.Atoi("42")
if err != nil {
fmt.Printf("couldn't convert number: %v\n", err)
return
}
fmt.Println("Converted integer:", i)
// custom error
import "time"
type MyError struct {
When time.Time
What string
}
func (e *MyError) Error() string {
return fmt.Sprintf("at %v, %s", e.When, e.What)
}
func run() error {
return &MyError{ time.Now(), "it didn't work", }
}
Generics
Go functions can be written to work on multiple types using type parameters. The type parameters of a function appear between brackets, before the function's arguments.
// https://go.dev/tour/generics/1
// generic function
func Index[T comparable](s []T, x T) int {
for i, v := range s {
// v and x are type T, which is `comparable`
if v == x {
return i
}
}
return -1
}
// generic type
type Stack[T any] struct {
data []T
}
Goroutines
A goroutine is a lightweight thread managed by the Go runtime.
// https://go.dev/tour/concurrency/1
func say(s string) {
for i := 0; i < 5; i++ {
time.Sleep(100 * time.Millisecond)
fmt.Println(s)
}
}
// `go` starts a new goroutine
func main() {
go say("world")
say("hello")
}
Channels
Channels are a typed conduit through which we can send and receive
values with the channel operator <-
. By default, sends and
receives block until the other side is ready.
// https://go.dev/tour/concurrency/2
func sum(s []int, c chan int) {
sum := 0
for _, v := range s {
sum += v
}
c <- sum // send sum to c
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
// make a channel of int
c := make(chan int)
// start two goroutines
go sum(s[:len(s)/2], c)
go sum(s[len(s)/2:], c)
x, y := <-c, <-c // receive from c
fmt.Println(x, y, x+y)
}
A sender can close a channel to indicate that no more values will be sent. Receivers can test whether a channel has been closed by assigning a second parameter to the receive expression.
// https://go.dev/tour/concurrency/4
func fibonacci(n int, c chan int) {
x, y := 0, 1
for i := 0; i < n; i++ {
c <- x
x, y = y, x+y
}
close(c) // close the channel
// can be tested by `v, ok := <-c`
}
func main() {
// make a buffered channel
c := make(chan int, 10)
go fibonacci(cap(c), c)
// receive from the channel until it's closed
for i := range c {
fmt.Println(i)
}
}
Select
The select
statement lets a goroutine wait on multiple communication
operations. A select
blocks until one of its cases can run, then it
executes that case. It chooses one at random if multiple are ready.
// https://go.dev/tour/concurrency/6
func main() {
tick := time.Tick(100 * time.Millisecond)
boom := time.After(500 * time.Millisecond)
for {
select {
case <-tick:
fmt.Println("tick.")
case <-boom:
fmt.Println("BOOM!")
return
default:
fmt.Println(" .")
time.Sleep(50 * time.Millisecond)
}
}
}