github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/talks/2015/tricks.slide (about) 1 Stupid Gopher Tricks 2 GolangUK 3 21 Aug 2015 4 5 Andrew Gerrand 6 adg@golang.org 7 8 9 * Video 10 11 A video of this talk was recorded at GolangUK in London. 12 13 .link https://www.youtube.com/watch?v=UECh7X07m6E Watch the talk on YouTube 14 15 16 * This talk 17 18 This talk is about things you might not know about Go. 19 20 Some of this stuff you can add to your Go vocabulary. 21 22 Other things are probably best left for special occasions. 23 24 25 * Language 26 27 28 * Type literals (1/2) 29 30 Here's a familiar type declaration: 31 32 type Foo struct { 33 i int 34 s string 35 } 36 37 The latter part of a type declaration is the *type*literal*: 38 39 struct { 40 i int 41 s string 42 } 43 44 Other examples of type literals are `int` and `[]string`, 45 which can also be declared as named types: 46 47 type Bar int 48 type Qux []string 49 50 51 * Type literals (2/2) 52 53 While we commonly use `int` and `[]string` in variable declarations: 54 55 var i int 56 var s []string 57 58 It is less common (but equally valid) to do the same with structs: 59 60 var t struct { 61 i int 62 s string 63 } 64 65 An unnamed struct literal is often called an *anonymous*struct*. 66 67 68 * Anonymous structs: template data 69 70 A common use is providing data to templates: 71 72 .play tricks/template.go /BEGIN/,/END/ 73 74 You could also use `map[string]interface{}`, 75 but then you sacrifice performance and type safety. 76 77 78 * Anonymous structs: JSON (1/2) 79 80 The same technique can be used for encoding JSON objects: 81 82 .play tricks/json-encode.go /Marshal/,/Print/ 83 84 And also decoding: 85 86 .play tricks/json-decode.go /data/,/Print/ 87 88 89 * Anonymous structs: JSON (2/2) 90 91 Structs can be nested to describe more complex JSON objects: 92 93 .play tricks/json-nest.go /data/,/Print/ 94 95 96 * Repeated literals and struct names 97 98 In repeated literals like slices and maps, Go lets you omit the inner type name: 99 100 .code tricks/repeated.go /BEGIN/,/END/ 101 102 103 * Repeated literals and anonymous structs 104 105 Combined with anonymous structs, this convenience shortens the code dramatically: 106 107 .code tricks/repeated2.go /BEGIN/,/END/ 108 109 110 * Anonymous structs: test cases (1/2) 111 112 These properties enable a nice way to express test cases: 113 114 .code tricks/string_test.go /TestIndex/,/^}/ 115 116 117 * Anonymous structs: test cases (2/2) 118 119 You can go a step further and put the composite literal in the range statement itself: 120 121 .code tricks/string_test2.go /TestIndex/,/^}/ 122 123 But this is harder to read. 124 125 126 * Embedded fields 127 128 A struct field that has no name is an *embedded*field*. 129 The embedded type's methods (and fields, if it is a struct) 130 are accessible as if they are part of the embedding struct. 131 132 .play tricks/embed.go /BEGIN/,$ 133 134 135 * Anonymous structs: embedded mutex 136 137 Of course, you can embed fields in an anonymous struct. 138 139 It's common to protect a global variable with a mutex variable: 140 141 var ( 142 viewCount int64 143 viewCountMu sync.Mutex 144 ) 145 146 By embedding a mutex in an anonymous struct, we can group the related values: 147 148 var viewCount struct { 149 sync.Mutex 150 n int64 151 } 152 153 Users of `viewCount` access it like this: 154 155 viewCount.Lock() 156 viewCount.n++ 157 viewCount.Unlock() 158 159 160 161 * Anonymous structs: implementing interfaces 162 163 And you can embed interfaces, too. 164 165 Here's a real example from [[https://camlistore.org/][Camlistore]]: 166 167 The function is expected to return a `ReadSeekCloser`, 168 but the programmer only had a string. 169 170 Anonymous struct (and its standard library friends) to the rescue! 171 172 return struct { 173 io.ReadSeeker 174 io.Closer 175 }{ 176 io.NewSectionReader(strings.NewReader(s), 0, int64(len(s))), 177 ioutil.NopCloser(nil), 178 } 179 180 181 * Anonymous interfaces 182 183 Interfaces can be anonymous, the most common being `interface{}`. 184 185 But the interface needn't be empty: 186 187 .play tricks/anon-interface.go /var s/,/Println/ 188 189 Useful for a sly type assertion (from `src/os/exec/exec_test.go`): 190 191 // Check that we can access methods of the underlying os.File. 192 if _, ok := stdin.(interface { 193 Fd() uintptr 194 }); !ok { 195 t.Error("can't access methods of underlying *os.File") 196 } 197 198 199 * Method values 200 201 A "method value" is what you get when you evaluate a method as an expression. 202 The result is a function value. 203 204 Evaluating a method from a _type_ yields a function: 205 206 .play tricks/method-values-1.go /var f/,/Stdout/ 207 208 Evaluating a method from a _value_ creates a closure that holds that value: 209 210 .play tricks/method-values-2.go /var /,/Stdout/ 211 212 213 * Method values: sync.Once 214 215 The `sync.Once` type is used to perform a task once with concurrency safety. 216 217 // Once is an object that will perform exactly one action. 218 type Once struct { /* Has unexported fields. */ } 219 220 func (o *Once) Do(f func()) 221 222 This `LazyPrimes` type computes a slice of prime numbers the first time it is used: 223 224 .code tricks/method-once.go /type/,$ 225 226 227 * Method values: HTTP handlers 228 229 You can use method values to implement multiple HTTP handlers with one type: 230 231 .code tricks/method-http.go /type Server/,$ 232 233 234 * Method values: another example 235 236 In package `os/exec`, the `Cmd` type implements methods to set up 237 standard input, output, and error: 238 239 func (c *Cmd) stdin() (f *os.File, err error) 240 func (c *Cmd) stdout() (f *os.File, err error) 241 func (c *Cmd) stderr() (f *os.File, err error) 242 243 The caller handles each in the same way, 244 so it iterates over a slice of method values: 245 246 type F func(*Cmd) (*os.File, error) 247 for _, setupFd := range []F{(*Cmd).stdin, (*Cmd).stdout, (*Cmd).stderr} { 248 fd, err := setupFd(c) 249 if err != nil { 250 c.closeDescriptors(c.closeAfterStart) 251 c.closeDescriptors(c.closeAfterWait) 252 return err 253 } 254 c.childFiles = append(c.childFiles, fd) 255 } 256 257 258 * Comparable types 259 260 The Go spec defines a set of types as "comparable"; 261 they may be compared with == and !=. 262 263 Bools, ints, floats, complex numbers, strings, pointers, 264 channels, *structs*, and *interfaces* are comparable. 265 266 .play tricks/compare.go /BEGIN/,/END/ 267 268 A struct is comparable only if its fields are comparable: 269 270 .play tricks/compare2.go /BEGIN/,/END/ 271 272 273 * Comparable types and map keys 274 275 Any comparable type may be used as a map key. 276 277 .code tricks/compare-map.go /BEGIN/,/END/ 278 279 280 * Structs as map keys 281 282 An example from the Go continuous build infrastructure: 283 284 type builderRev struct { 285 builder, rev string 286 } 287 288 var br = builderRev{"linux-amd64", "0cd299"} 289 290 We track in-flight builds in a map. 291 The pre-Go 1 way was to flatten the data to a string first: 292 293 inflight := map[string]bool{} 294 295 inflight[br.builder + "-" + br.rev] = true 296 297 But with struct keys, you can avoid the allocation and have cleaner code: 298 299 inflight := map[builderRev]bool{} 300 301 inflight[br] = true 302 303 304 * Interfaces as map keys 305 306 An example of interface map keys from Docker's `broadcastwriter` package: 307 308 .code tricks/broadcastwriter/broadcastwriter.go /type/,/END/ 309 310 311 * Structs and interfaces together as map keys 312 313 A (very) contrived example: (Don't do this! Ever!) 314 315 .play tricks/cons.go /type/,$ 316 317 318 * Libraries 319 320 * sync/atomic 321 322 For a simple counter, you can use the `sync/atomic` package's 323 functions to make atomic updates without the lock. 324 325 func AddInt64(addr *int64, delta int64) (new int64) 326 func CompareAndSwapInt64(addr *int64, old, new int64) (swapped bool) 327 func LoadInt64(addr *int64) (val int64) 328 func StoreInt64(addr *int64, val int64) 329 func SwapInt64(addr *int64, new int64) (old int64) 330 331 First, define the global variable (appropriately documented!): 332 333 // viewCount must be updated atomically. 334 var viewCount int64 335 336 Then increment it with `AddInt64`: 337 338 count := atomic.AddInt64(&viewCount, 1) 339 340 The set are available for `Int32`, `Uint32`, `Int64`, `Uint64`, `Pointer`, and `Uintptr`. 341 342 343 * sync/atomic.Value 344 345 Another option for sharing state is `atomic.Value`. 346 347 For instance, to share configuration between many goroutines: 348 349 type Config struct { 350 Timeout time.Duration 351 } 352 353 var config atomic.Value 354 355 To set or update, use the `Store` method: 356 357 config.Store(&Config{Timeout: 2*time.Second}) 358 359 To read, each goroutine calls the `Load` method: 360 361 cfg := config.Load().(*Config) 362 363 Note that storing different types in the same `Value` will cause a panic. 364 365 366 * sync/atomic.Value: how it works (1/5) 367 368 The `atomic.Value` primitive is the size of a single interface value: 369 370 package atomic 371 372 type Value struct { 373 v interface{} 374 } 375 376 An interface value is represented by the runtime as two pointers: 377 one for the type, and one for the value. 378 379 // ifaceWords is interface{} internal representation. 380 type ifaceWords struct { 381 typ unsafe.Pointer 382 data unsafe.Pointer 383 } 384 385 To load and store an interface value atomically, it operates on the parts of the interface value with `atomic.LoadPointer` and `atomic.StorePointer`. 386 387 388 * sync/atomic.Value: how it works (2/5) 389 390 The `Store` method first validates the input: 391 392 func (v *Value) Store(x interface{}) { 393 if x == nil { 394 panic("sync/atomic: store of nil value into Value") 395 } 396 397 Then uses `unsafe` to cast the current and new `interface{}` values to `ifaceWords`: 398 399 // ... 400 vp := (*ifaceWords)(unsafe.Pointer(v)) 401 xp := (*ifaceWords)(unsafe.Pointer(&x)) 402 403 (This allows us to get at the internals of those interface values.) 404 405 406 * sync/atomic.Value: how it works (3/5) 407 408 Spin while loading the type field: 409 410 // ... 411 for { 412 typ := LoadPointer(&vp.typ) 413 414 If it's `nil` the this is the first time the value has been stored. 415 Put a sentinel value (max uintptr) in the type field to "lock" it while we work with it: 416 417 // ... 418 if typ == nil { 419 if !CompareAndSwapPointer(&vp.typ, nil, unsafe.Pointer(^uintptr(0))) { 420 continue // Someone beat us to it. Wait. 421 } 422 423 Store the data field, then the type field, and we're done! 424 425 // ... 426 StorePointer(&vp.data, xp.data) 427 StorePointer(&vp.typ, xp.typ) 428 return 429 } 430 431 * sync/atomic.Value: how it works (4/5) 432 433 If this isn't the first store, check whether a store is already happening: 434 435 // ... 436 if uintptr(typ) == ^uintptr(0) { 437 continue // First store in progress. Wait. 438 } 439 440 Sanity check whether the type changed: 441 442 // ... 443 if typ != xp.typ { 444 panic("sync/atomic: store of inconsistently typed value into Value") 445 } 446 447 If the type field is what we expect, go ahead and atomically store the value: 448 449 // ... 450 StorePointer(&vp.data, xp.data) 451 return 452 } 453 } 454 455 * sync/atomic.Value: how it works (5/5) 456 457 The `Load` method first loads the interface's type field: 458 459 func (v *Value) Load() (x interface{}) { 460 vp := (*ifaceWords)(unsafe.Pointer(v)) 461 typ := LoadPointer(&vp.typ) 462 463 Then, check whether a store has happened, or is happening: 464 465 // ... 466 if typ == nil || uintptr(typ) == ^uintptr(0) { 467 return nil 468 } 469 470 Otherwise, load the data field and return both type and data as a new interface value: 471 472 // ... 473 data := LoadPointer(&vp.data) 474 xp := (*ifaceWords)(unsafe.Pointer(&x)) 475 xp.typ = typ 476 xp.data = data 477 return 478 } 479 480 481 * A note on sync/atomic 482 483 Usually you don't want or need the stuff in this package. 484 485 Try channels or the `sync` package first. 486 487 You almost certainly shouldn't write code like the `atomic.Value` implementation. 488 (And I didn't show all of it; the real code has hooks into the runtime.) 489 490 491 * Tools 492 493 494 * Testing 495 496 * Subprocess tests 497 498 Sometimes you need to test the behavior of a process, not just a function. 499 500 .code tricks/subprocess/subprocess.go /func Crasher/,/^}/ 501 502 To test this code, we invoke the test binary itself as a subprocess: 503 504 .code tricks/subprocess/subprocess_test.go /func TestCrasher/,/^}/ 505 506 507 * Subprocess benchmarks (1/2) 508 509 Go's CPU and memory profilers report data for an entire process. 510 To profile just one side of a concurrent operation, you can use a sub-process. 511 512 This benchmark from the `net/http` package spawns a child process to make 513 requests to a server running in the main process. 514 515 func BenchmarkServer(b *testing.B) { 516 b.ReportAllocs() 517 // Child process mode; 518 if url := os.Getenv("TEST_BENCH_SERVER_URL"); url != "" { 519 n, err := strconv.Atoi(os.Getenv("TEST_BENCH_CLIENT_N")) 520 if err != nil { 521 panic(err) 522 } 523 for i := 0; i < n; i++ { 524 res, err := Get(url) 525 // ... 526 } 527 os.Exit(0) 528 return 529 } 530 // ... 531 532 * Subprocess benchmarks (2/2) 533 534 // ... 535 res := []byte("Hello world.\n") 536 ts := httptest.NewServer(HandlerFunc(func(rw ResponseWriter, r *Request) { 537 rw.Header().Set("Content-Type", "text/html; charset=utf-8") 538 rw.Write(res) 539 })) 540 defer ts.Close() 541 542 cmd := exec.Command(os.Args[0], "-test.run=XXXX", "-test.bench=BenchmarkServer$") 543 cmd.Env = append([]string{ 544 fmt.Sprintf("TEST_BENCH_CLIENT_N=%d", b.N), 545 fmt.Sprintf("TEST_BENCH_SERVER_URL=%s", ts.URL), 546 }, os.Environ()...) 547 if out, err := cmd.CombinedOutput(); err != nil { 548 b.Errorf("Test failure: %v, with output: %s", err, out) 549 } 550 } 551 552 To run: 553 554 $ go test -run=XX -bench=BenchmarkServer -benchtime=15s -cpuprofile=http.prof 555 $ go tool pprof http.test http.prof 556 557 558 * go list 559 560 * go list 561 562 $ go help list 563 usage: go list [-e] [-f format] [-json] [build flags] [packages] 564 565 List lists the packages named by the import paths, one per line. 566 567 Show the packages under a path: 568 569 $ go list golang.org/x/oauth2/... 570 golang.org/x/oauth2 571 ... 572 golang.org/x/oauth2/vk 573 574 Show the standard library: 575 576 $ go list std 577 archive/tar 578 ... 579 unsafe 580 581 Show all packages: 582 583 $ go list all 584 585 * go list -json 586 587 The `-json` flag tells you everything the go tool knows about a package: 588 589 $ go list -json bytes 590 { 591 "Dir": "/Users/adg/go/src/bytes", 592 "ImportPath": "bytes", 593 "Name": "bytes", 594 "Doc": "Package bytes implements functions for the manipulation of byte slices.", 595 "Target": "/Users/adg/go/pkg/darwin_amd64/bytes.a", 596 "Goroot": true, 597 "Standard": true, 598 "Stale": true, 599 "Root": "/Users/adg/go", 600 "GoFiles": [ 601 "buffer.go", 602 "bytes.go", 603 "bytes_decl.go", 604 "reader.go" 605 ], 606 ... 607 } 608 609 It's easy to write programs that consume this data. 610 611 * go list's Package struct (1/3) 612 613 The go tool's documented `Package` struct describes all the possible fields: 614 615 type Package struct { 616 Dir string // directory containing package sources 617 ImportPath string // import path of package in dir 618 ImportComment string // path in import comment on package statement 619 Name string // package name 620 Doc string // package documentation string 621 Target string // install path 622 Shlib string // the shared library that contains this package (only set when -linkshared) 623 Goroot bool // is this package in the Go root? 624 Standard bool // is this package part of the standard Go library? 625 Stale bool // would 'go install' do anything for this package? 626 Root string // Go root or Go path dir containing this package 627 628 // (more fields on next slide) 629 } 630 631 * go list's Package struct (2/3) 632 633 type Package struct { 634 // (more fields on previous slide) 635 636 // Source files 637 GoFiles []string // .go source files (excluding CgoFiles, TestGoFiles, XTestGoFiles) 638 CgoFiles []string // .go sources files that import "C" 639 IgnoredGoFiles []string // .go sources ignored due to build constraints 640 CFiles []string // .c source files 641 CXXFiles []string // .cc, .cxx and .cpp source files 642 MFiles []string // .m source files 643 HFiles []string // .h, .hh, .hpp and .hxx source files 644 SFiles []string // .s source files 645 SwigFiles []string // .swig files 646 SwigCXXFiles []string // .swigcxx files 647 SysoFiles []string // .syso object files to add to archive 648 649 // (more fields on next slide) 650 } 651 652 * go list's Package struct (3/3) 653 654 type Package struct { 655 // (more fields on previous slide) 656 657 // Cgo directives 658 CgoCFLAGS []string // cgo: flags for C compiler 659 CgoCPPFLAGS []string // cgo: flags for C preprocessor 660 CgoCXXFLAGS []string // cgo: flags for C++ compiler 661 CgoLDFLAGS []string // cgo: flags for linker 662 CgoPkgConfig []string // cgo: pkg-config names 663 664 // Dependency information 665 Imports []string // import paths used by this package 666 Deps []string // all (recursively) imported dependencies 667 668 // Error information 669 Incomplete bool // this package or a dependency has an error 670 Error *PackageError // error loading package 671 DepsErrors []*PackageError // errors loading dependencies 672 673 TestGoFiles []string // _test.go files in package 674 TestImports []string // imports from TestGoFiles 675 XTestGoFiles []string // _test.go files outside package 676 XTestImports []string // imports from XTestGoFiles 677 } 678 679 * go list -f (1/2) 680 681 That's a ton of information, so what can we do with it? 682 683 The `-f` flag lets you use Go's `text/template` package to format the ouput. 684 685 Show package doc strings: 686 687 $ go list -f '{{.Doc}}' golang.org/x/oauth2/... 688 Package oauth2 provides support for making OAuth2 authorized and authenticated HTTP requests. 689 Package jwt implements the OAuth 2.0 JSON Web Token flow, commonly known as "two-legged OAuth 2.0". 690 ... 691 692 Show the Go files in a package: 693 694 $ go list -f '{{.GoFiles}}' bytes 695 [buffer.go bytes.go bytes_decl.go reader.go] 696 697 Make the output cleaner with a `join`: 698 699 $ go list -f '{{join .GoFiles " "}}' bytes 700 buffer.go bytes.go bytes_decl.go reader.go 701 702 * go list -f (2/2) 703 704 With template logic we can test packages for certain conditions. 705 706 Find standard libraries that lack documentation: 707 708 $ go list -f '{{if not .Doc}}{{.ImportPath}}{{end}}' std 709 internal/format 710 internal/trace 711 712 Find packages that depend (directly or indirectly) on a given package: 713 714 $ go list -f '{{range .Deps}}{{if eq . "golang.org/x/oauth2"}}{{$.ImportPath}}{{end}}{{end}}' all 715 golang.org/x/build/auth 716 golang.org/x/build/buildlet 717 golang.org/x/build/cmd/buildlet 718 ... 719 720 Find packages that are broken somehow (note `-e`): 721 722 $ go list -e -f '{{with .Error}}{{.}}{{end}}' all 723 package github.com/golang/oauth2: code in directory /Users/adg/src/github.com/golang/oauth2 724 expects import "golang.org/x/oauth2" 725 ... 726 727 * go list and the shell 728 729 Things get interesting once we start using the shell. 730 731 For instance, we can use `go` `list` output as input to the go tool. 732 733 Test all packages except vendored packages: 734 735 $ go test $(go list ./... | grep -v '/vendor/') 736 737 Test the dependencies of a specific package: 738 739 $ go test $(go list -f '{{join .Deps " "}}' golang.org/x/oauth2) 740 741 The same, but don't test the standard library: 742 743 $ go test $( 744 go list -f '{{if (not .Goroot)}}{{.ImportPath}}{{end}}' $( 745 go list -f '{{join .Deps " "}}' golang.org/x/oauth2 746 ) 747 ) 748 749 * go list printing line counts 750 751 for pkg in $(go list golang.org/x/oauth2/...); do 752 wc -l $(go list -f '{{range .GoFiles}}{{$.Dir}}/{{.}} {{end}}' $pkg) | \ 753 tail -1 | awk '{ print $1 " '$pkg'" }' 754 done | sort -nr 755 756 The output: 757 758 617 golang.org/x/oauth2/google 759 600 golang.org/x/oauth2 760 357 golang.org/x/oauth2/internal 761 160 golang.org/x/oauth2/jws 762 147 golang.org/x/oauth2/jwt 763 112 golang.org/x/oauth2/clientcredentials 764 22 golang.org/x/oauth2/paypal 765 16 golang.org/x/oauth2/vk 766 16 golang.org/x/oauth2/odnoklassniki 767 16 golang.org/x/oauth2/linkedin 768 16 golang.org/x/oauth2/github 769 16 golang.org/x/oauth2/facebook 770 771 * go list generating dependency graphs 772 773 ( echo "digraph G {" 774 go list -f '{{range .Imports}}{{printf "\t%q -> %q;\n" $.ImportPath .}}{{end}}' \ 775 $(go list -f '{{join .Deps " "}}' time) time 776 echo "}" 777 ) | dot -Tsvg -o time-deps.svg 778 779 .image tricks/time-deps.png 400 _ 780 781 782 * And there's more! 783 784 There are many more fun corners of Go. 785 Can you find them all? :-) 786 787 Read the docs, explore, and have fun!