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