10 things you (probably) don't know about Go
Andrew Gerrand
Gopher
Andrew Gerrand
Gopher
var config struct {
APIKey string
OAuthConfig oauth.Config
}
config.APIKey = "BADC0C0A"data := struct {
Title string
Users []*User
}{
title,
users,
}
err := tmpl.Execute(w, data)
(Cheaper and safer than using map[string]interface{}.)
var indexRuneTests = []struct {
s string
rune rune
out int
}{
{"a A x", 'A', 2},
{"some_text=some_value", '=', 9},
{"☺a", 'a', 3},
{"a☻☺b", '☺', 4},
}var hits struct {
sync.Mutex
n int
}
hits.Lock()
hits.n++
hits.Unlock(){"data": {"children": [
{"data": {
"title": "The Go homepage",
"url": "https://go.dev/"
}},
...
]}}type Item struct {
Title string
URL string
}
type Response struct {
Data struct {
Children []struct {
Data Item
}
}
}% godoc sync Mutex
PACKAGE
package sync
import "sync"
TYPES
type Mutex struct {
// contains filtered or unexported fields
}
A Mutex is a mutual exclusion lock. Mutexes can be created as part of
other structures; the zero value for a Mutex is an unlocked mutex.
func (m *Mutex) Lock()
Lock locks m. If the lock is already in use, the calling goroutine
blocks until the mutex is available.
func (m *Mutex) Unlock()
Unlock unlocks m. It is a run-time error if m is not locked on entry to
Unlock.
A locked Mutex is not associated with a particular goroutine. It is
allowed for one goroutine to lock a Mutex and then arrange for another
goroutine to unlock it.% godoc -src sync Mutex
// A Mutex is a mutual exclusion lock.
// Mutexes can be created as part of other structures;
// the zero value for a Mutex is an unlocked mutex.
type Mutex struct {
state int32
sema uint32
}Also shows unexported state! Great for digging around.
6Yep:
go get camlistore.org/pkg/netutil
See `go help importpath` for the details.
7Got a package that works with the file system, but don't want your tests to actually use the disk?
var fs fileSystem = osFS{}
type fileSystem interface {
Open(name string) (file, error)
Stat(name string) (os.FileInfo, error)
}
type file interface {
io.Closer
io.Reader
io.ReaderAt
io.Seeker
Stat() (os.FileInfo, error)
}
// osFS implements fileSystem using the local disk.
type osFS struct{}
func (osFS) Open(name string) (file, error) { return os.Open(name) }
func (osFS) Stat(name string) (os.FileInfo, error) { return os.Stat(name) }
Implement your own fake fileSystem in and put it in fs while testing.
type T struct {}
func (T) Foo(s string) { println(s) }
var fn func(T, string) = T.Foo
Real example from os/exec:
func (c *Cmd) stdin() (f *os.File, err error) func (c *Cmd) stdout() (f *os.File, err error) func (c *Cmd) stderr() (f *os.File, err error)
type F func(*Cmd) (*os.File, error)
for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} {
fd, err := setupFd(c)
if err != nil {
c.closeDescriptors(c.closeAfterStart)
c.closeDescriptors(c.closeAfterWait)
return err
}
c.childFiles = append(c.childFiles, fd)
}package main import "fmt" var battle = make(chan string) func warrior(name string, done chan struct{}) { select { case opponent := <-battle: fmt.Printf("%s beat %s\n", name, opponent) case battle <- name: // I lost :-( } done <- struct{}{} } func main() { done := make(chan struct{}) langs := []string{"Go", "C", "C++", "Java", "Perl", "Python"} for _, l := range langs { go warrior(l, done) } for _ = range langs { <-done } }
// +build ignore,OMIT
package main
import (
"fmt"
"time"
"math/rand"
)
func waiter(i int, block, done chan struct{}) { time.Sleep(time.Duration(rand.Intn(3000)) * time.Millisecond) fmt.Println(i, "waiting...") <-block fmt.Println(i, "done!") done <- struct{}{} } func main() { block, done := make(chan struct{}), make(chan struct{}) for i := 0; i < 4; i++ { go waiter(i, block, done) } time.Sleep(5 * time.Second) close(block) for i := 0; i < 4; i++ { <-done } }
// +build ignore,OMIT
package main
import (
"fmt"
"math/rand"
"time"
)
func worker(i int, ch chan Work, quit chan struct{}) { var quitting bool for { select { case w := <-ch: if quitting { w.Refuse(); fmt.Println("worker", i, "refused", w) break } w.Do(); fmt.Println("worker", i, "processed", w) case <-quit: fmt.Println("worker", i, "quitting") quitting = true } } } func main() { ch, quit := make(chan Work), make(chan struct{}) go makeWork(ch) for i := 0; i < 4; i++ { go worker(i, ch, quit) } time.Sleep(5 * time.Second) close(quit) time.Sleep(2 * time.Second) }
type Work string
func (w Work) Do() {
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
}
func (w Work) Refuse() {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
}
func makeWork(ch chan Work) {
for i := 0; ; i++ {
ch <- Work(fmt.Sprintf("job %x", i))
}
}
// +build ignore,OMIT
package main
import (
"fmt"
"math/rand"
"time"
)
func worker(i int, ch chan Work, quit chan struct{}) { for { select { case w := <-ch: if quit == nil { w.Refuse(); fmt.Println("worker", i, "refused", w) break } w.Do(); fmt.Println("worker", i, "processed", w) case <-quit: fmt.Println("worker", i, "quitting") quit = nil } } } func main() { ch, quit := make(chan Work), make(chan struct{}) go makeWork(ch) for i := 0; i < 4; i++ { go worker(i, ch, quit) } time.Sleep(5 * time.Second) close(quit) time.Sleep(2 * time.Second) }
type Work string
func (w Work) Do() {
time.Sleep(time.Duration(rand.Intn(1000)) * time.Millisecond)
}
func (w Work) Refuse() {
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond)
}
func makeWork(ch chan Work) {
for i := 0; ; i++ {
ch <- Work(fmt.Sprintf("job %x", i))
}
}