github.com/ethersphere/bee/v2@v2.2.0/CODINGSTYLE.md (about) 1 # Go Style Guide 2 3 - [Go Style Guide](#go-style-guide) 4 - [Consistent Spelling and Naming](#consistent-spelling-and-naming) 5 - [Code Formatting](#code-formatting) 6 - [Unused Names](#unused-names) 7 - [Naked returns and Named Parameters](#naked-returns-and-named-parameters) 8 - [Testing](#testing) 9 - [Parallel Test Execution](#parallel-test-execution) 10 - [Naming Tests](#naming-tests) 11 - [Group Declarations by Meaning](#group-declarations-by-meaning) 12 - [Make Zero-value Useful](#make-zero-value-useful) 13 - [Beware of Copying Mutexes in Go](#beware-of-copying-mutexes-in-go) 14 - [Copy Slices and Maps at Boundaries](#copy-slices-and-maps-at-boundaries) 15 - [Receiving Slices and Maps](#receiving-slices-and-maps) 16 - [Returning Slices and Maps](#returning-slices-and-maps) 17 - [Filtering in place](#filtering-in-place) 18 - [Pointers to Interfaces](#pointers-to-interfaces) 19 - [Verify Interface Compliance](#verify-interface-compliance) 20 - [Receivers and Interfaces](#receivers-and-interfaces) 21 - [Defer to Clean Up](#defer-to-clean-up) 22 - [Channel Size is One or None](#channel-size-is-one-or-none) 23 - [Start Enums at One](#start-enums-at-one) 24 - [Use `"time"` to handle time](#use-time-to-handle-time) 25 - [Use `time.Time` for instants of time](#use-timetime-for-instants-of-time) 26 - [Use `time.Duration` for periods of time](#use-timeduration-for-periods-of-time) 27 - [Use `time.Time` and `time.Duration` with external systems](#use-timetime-and-timeduration-with-external-systems) 28 - [Error Types](#error-types) 29 - [Error Wrapping](#error-wrapping) 30 - [Handle Type Assertion Failures](#handle-type-assertion-failures) 31 - [Panic with care](#panic-with-care) 32 - [Avoid Mutable Globals](#avoid-mutable-globals) 33 - [Avoid Embedding Types in Public Structs](#avoid-embedding-types-in-public-structs) 34 - [Avoid Using Built-In Names](#avoid-using-built-in-names) 35 - [Avoid `init()`](#avoid-init) 36 - [Exit in Main](#exit-in-main) 37 - [Exit Once](#exit-once) 38 - [Performance](#performance) 39 - [Prefer strconv over fmt](#prefer-strconv-over-fmt) 40 - [Avoid string-to-byte conversion](#avoid-string-to-byte-conversion) 41 - [Prefer Specifying Container Capacity](#prefer-specifying-container-capacity) 42 - [Specifying Map Capacity Hints](#specifying-map-capacity-hints) 43 - [Specifying Slice Capacity](#specifying-slice-capacity) 44 45 ## Consistent Spelling and Naming 46 47 Naming is difficult, but prefer the consistency of naming throughout the code base. 48 If a naming pattern is already established in the codebase, follow it. If you are unsure, look in the Golang standard library for inspiration. 49 It's similar to the `gofmt` tool, the formatting isn't to everyone's liking, but it is consistent. 50 51 Prefer american spellings over British spellings, avoid Latin abbreviations. 52 53 *Note:* `misspell` linter should take care of these, but it's good to keep in mind. 54 55 <table> 56 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 57 <tbody> 58 <tr><td> 59 60 ```go 61 // marshalling 62 // unmarshalling 63 // cancelling 64 // cancelled 65 // cancellation 66 ``` 67 68 </td><td> 69 70 ```go 71 // marshaling 72 // unmarshaling 73 // canceling 74 // canceled 75 // cancellation 76 ``` 77 78 </td></tr> 79 </tbody></table> 80 81 ## Code Formatting 82 83 - Keep line length, argument count and function size reasonable. 84 - Run `make lint-style` command to lint the codebase using a set of style linters. 85 - Run `make format FOLDER=pkg/mypackage` to format the code using `gci` and `gfumpt` formatters. 86 87 ## Unused Names 88 89 Avoid unused method receiver names. 90 91 <table> 92 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 93 <tbody> 94 <tr><td> 95 96 ```go 97 func (f foo) method() { 98 // no references to f 99 } 100 101 func method(_ string) { 102 ... 103 } 104 ``` 105 106 </td><td> 107 108 ```go 109 func (foo) method() { 110 ... 111 } 112 113 func method(string) { 114 ... 115 } 116 ``` 117 118 </td></tr> 119 </tbody></table> 120 121 Note: there might be a linter to take care of this 122 123 ## Naked returns and Named Parameters 124 125 Don't name result parameters just to avoid declaring a var inside the function; that trades off a minor implementation brevity at the cost of unnecessary API verbosity. 126 127 Naked returns are okay if the function is a handful of lines. Once it's a medium sized function, be explicit with your return values. 128 129 ```go 130 func collect(birds ...byte) (ducks []byte) { 131 for _, bird := range birds { 132 if isDuck(bird) { 133 ducks = append(ducks, bird) 134 } 135 } 136 137 return 138 } 139 ``` 140 141 *Corollary:* it's not worth it to name result parameters just because it enables you to use naked returns. Clarity of docs is always more important than saving a line or two in your function. 142 143 Finally, in some cases you need to name a result parameter in order to change it in a deferred closure. That is always OK. 144 145 ```go 146 func getID() (id int, err error) { 147 defer func() { 148 if err != nil { 149 err = fmt.Errorf("some extra info: %v", err) 150 } 151 } 152 153 id, err = /* call to db */ 154 155 return 156 } 157 ``` 158 ## Testing 159 160 Use the Golang [testing package](https://pkg.go.dev/testing) from the standard library for writing tests. 161 162 ### Parallel Test Execution 163 164 Run tests in parallel where possible but don't forget about variable scope gotchas. 165 166 ```go 167 for tc := range tt { 168 tc := tc // must not forget this 169 t.Run(tc.name, func(t *testing.T) { 170 t.Parallel() 171 //execute 172 // ... 173 // assert 174 if got != tt.want { 175 // useful for human comparable values 176 t.Errorf("wrong output\ngot: %q\nwant: %q", got, want) 177 // alternatively 178 t.Errorf("Foo(%q) = %d; want %d", tt.in, got, tt.want) 179 } 180 }) 181 } 182 ``` 183 184 ### Naming Tests 185 186 Name tests with a compact name that reflects their scenario. Don't try to specify the scenario in the test name, that is not what it's for. Use the accompanying godoc to describe the test scenario. 187 188 <table> 189 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 190 <tbody> 191 <tr><td> 192 193 ```go 194 func TestSomethingBySettingVarToFive(t *testing.T) { 195 ... 196 } 197 ``` 198 199 </td><td> 200 201 ```go 202 // TestSomething tests that something works correctly by doing this and that. 203 func TestSomething(t *testing.T) { 204 ... 205 } 206 ``` 207 208 </td> 209 </td></tr> 210 </tbody></table> 211 212 If needed, use an underscore to disambiguate tests that are hard to name: 213 ```go 214 func TestScenario_EdgeCase(t *testing.T) { 215 ... 216 } 217 218 func TestScenario_CornerCase(t *testing.T) { 219 ... 220 } 221 ``` 222 223 Ideally, try to use nested tests that would cause the test runner to automatically assemble the different test cases in separate entries: 224 225 ```go 226 func TestSomething(t *testing.T) { 227 ... 228 t.Run("edge case", func(t *testing.T) { ... }) 229 } 230 ``` 231 232 Avoid using the word "fail" when naming tests. Since the go test runner uses the same keyword to denote failed tests, this just prolongs the search for relevant information when inspecting build artifacts. 233 234 ## Group Declarations by Meaning 235 236 Where possible, group declarations by their purpose. 237 238 <table> 239 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 240 <tbody> 241 <tr><td> 242 243 ```go 244 const ( 245 limitDays = 90 246 warningDays = 0.9 * limitDays 247 sleepFor = 30 * time.Minute 248 ) 249 250 251 var ( 252 _ Interface = (*Accounting)(nil) 253 balancesPrefix = "accounting_balance_" 254 someOtherPrefix = "some_other_balance_" 255 ErrLimitExceeded = errors.New("limit exceeded") 256 ) 257 ``` 258 259 </td><td> 260 261 ```go 262 const ( 263 limitDays = 90 264 warningDays = 0.9 * limitDays 265 ) 266 267 const sleepFor = 30 * time.Minute 268 269 var _ Interface = (*Accounting)(nil) 270 271 var ( // or const 272 balancesPrefix = "accounting_balance_" 273 someOtherPrefix = "some_other_balance_" 274 ) 275 276 var ErrLimitExceeded = errors.New("limit exceeded") 277 ``` 278 279 </td></tr> 280 </tbody></table> 281 282 ## Make Zero-value Useful 283 284 - The zero-value of `sync.Mutex` and `sync.RWMutex` is valid, so you almost never need a pointer to a mutex. 285 286 <table> 287 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 288 <tbody> 289 <tr><td> 290 291 ```go 292 mu := new(sync.Mutex) 293 mu.Lock() 294 ``` 295 296 </td><td> 297 298 ```go 299 var mu sync.Mutex 300 mu.Lock() 301 ``` 302 303 </td></tr> 304 </tbody></table> 305 306 - For the same reason, a struct field of `sync.Mutex` does not require explicit initialization. 307 308 <table> 309 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 310 <tbody> 311 <tr><td> 312 313 ```go 314 type Store struct { 315 mu sync.Mutex 316 } 317 318 s := Store{ 319 mu: sync.Mutex{}, 320 } 321 322 // use s 323 ``` 324 325 </td><td> 326 327 ```go 328 type Store struct { 329 mu sync.Mutext 330 } 331 332 var s Store 333 334 335 336 // use s 337 ``` 338 339 </td></tr> 340 </tbody></table> 341 342 - The zero value (a slice declared with `var`) is usable immediately without `make()`. 343 344 <table> 345 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 346 <tbody> 347 <tr><td> 348 349 ```go 350 nums := []int{} 351 // or, nums := make([]int) 352 353 if add1 { 354 nums = append(nums, 1) 355 } 356 357 if add2 { 358 nums = append(nums, 2) 359 } 360 ``` 361 362 </td><td> 363 364 ```go 365 var nums []int 366 367 if add1 { 368 nums = append(nums, 1) 369 } 370 371 if add2 { 372 nums = append(nums, 2) 373 } 374 375 ``` 376 377 </td></tr> 378 </tbody></table> 379 380 `nil` is a valid slice of length 0. This means that, 381 382 - You should not return a slice of length zero explicitly. Return `nil` instead. 383 384 <table> 385 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 386 <tbody> 387 <tr><td> 388 389 ```go 390 if x == "" { 391 return []int{} 392 } 393 ``` 394 395 </td><td> 396 397 ```go 398 if x == "" { 399 return nil 400 } 401 ``` 402 403 </td></tr> 404 </tbody></table> 405 *Note:* in the case of serialization it might make sense to use a zero length initialized slice. For instance the JSON representation of a _nil_ slice is `null`, however a zero length allocated slice would be translated to `[]`. 406 407 - To check if a slice is empty, always use `len(s) == 0`. Do not check for `nil`. 408 409 <table> 410 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 411 <tbody> 412 <tr><td> 413 414 ```go 415 func isEmpty(s []string) bool { 416 return s == nil 417 } 418 ``` 419 420 </td><td> 421 422 ```go 423 func isEmpty(s []string) bool { 424 return len(s) == 0 425 } 426 ``` 427 428 </td></tr> 429 </tbody></table> 430 431 Remember that, while it is a valid slice, a `nil` slice is not equivalent to an allocated slice of length `0` - one is `nil` and the other is not - and the two may be treated differently in different situations (such as serialization and comparison). 432 433 ## Beware of Copying Mutexes in Go 434 435 The `sync.Mutex` is a value type so copying it is wrong. We're just creating a different mutex, so obviously the exclusion no longer works. 436 437 <table> 438 <thead><tr><th>Bad</th> <th>Good</th></tr></thead> 439 <tbody> 440 <tr> 441 <td> 442 443 ```go 444 type Container struct { 445 mu sync.Mutex 446 counters map[string]int 447 } 448 449 func (c Container) inc(name string) { // the value receiver will make a copy of the mutex 450 c.mu.Lock() 451 defer c.mu.Unlock() 452 c.counters[name]++ 453 } 454 ``` 455 456 </td> 457 <td> 458 459 ```go 460 type Container struct { 461 my.sync.Mutex 462 counters map[string]int 463 } 464 465 func (c *Container) inc(name string) { 466 c.mu.Lock() 467 defer c.mu.Unlock() 468 c.counters[name]++ 469 } 470 ``` 471 472 </td> 473 </tr> 474 475 </tbody> 476 </table> 477 478 ## Copy Slices and Maps at Boundaries 479 480 Slices and maps contain pointers to the underlying data so be wary of scenarios when they need to be copied. 481 482 ### Receiving Slices and Maps 483 484 Keep in mind that users can modify a map or slice you received as an argument if you store a reference to it. 485 486 <table> 487 <thead><tr><th>Bad</th> <th>Good</th></tr></thead> 488 <tbody> 489 <tr> 490 <td> 491 492 ```go 493 func (d *Driver) SetTrips(trips []Trip) { 494 d.trips = trips 495 } 496 497 trips := ... 498 d1.SetTrips(trips) 499 500 // Did you mean to modify d1.trips? 501 trips[0] = ... 502 ``` 503 504 </td> 505 <td> 506 507 ```go 508 func (d *Driver) SetTrips(trips []Trip) { 509 d.trips = make([]Trip, len(trips)) 510 copy(d.trips, trips) 511 } 512 513 trips := ... 514 d1.SetTrips(trips) 515 516 // We can now modify trips[0] without affecting d1.trips. 517 trips[0] = ... 518 ``` 519 520 </td> 521 </tr> 522 523 </tbody> 524 </table> 525 526 ### Returning Slices and Maps 527 528 Similarly, be wary of user modifications to maps or slices exposing internal state. 529 530 <table> 531 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 532 <tbody> 533 <tr><td> 534 535 ```go 536 type Stats struct { 537 mu sync.Mutex 538 counters map[string]int 539 } 540 541 // Snapshot returns the current stats. 542 func (s *Stats) Snapshot() map[string]int { 543 s.mu.Lock() 544 defer s.mu.Unlock() 545 546 return s.counters 547 } 548 549 550 551 // snapshot is no longer protected by the mutex, so any 552 // access to the snapshot is subject to data races. 553 snapshot := stats.Snapshot() 554 ``` 555 556 </td><td> 557 558 ```go 559 type Stats struct { 560 mu sync.Mutex 561 counters map[string]int 562 } 563 564 func (s *Stats) Snapshot() map[string]int { 565 s.mu.Lock() 566 defer s.mu.Unlock() 567 568 result := make(map[string]int, len(s.counters)) 569 for k, v := range s.counters { 570 result[k] = v 571 } 572 return result 573 } 574 575 // Snapshot is now a copy. 576 snapshot := stats.Snapshot() 577 ``` 578 579 </td></tr> 580 </tbody></table> 581 582 ### Filtering in place 583 584 This trick uses the fact that a slice shares the same backing array and capacity as the original, so the storage is reused for the filtered slice. Of course, the original contents are modified, so be mindful. It is useful for the code in the 'hot path' where we want to minimize allocation. 585 586 <table> 587 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 588 <tbody> 589 <tr><td> 590 591 ```go 592 var b []rune 593 for _, x := range a { 594 if f(x) { 595 b = append(b, x) // will cause new allocations 596 } 597 } 598 599 600 601 602 603 604 605 606 607 608 ``` 609 610 </td><td> 611 612 ```go 613 b := a[:0] 614 for _, x := range a { 615 if f(x) { 616 b = append(b, x) // will reuse the backing array 617 } 618 } 619 620 // alternatively using index, a bit more verbose 621 n := 0 622 for _, x := range a { 623 if f(x) { 624 a[n] = x 625 n++ 626 } 627 } 628 a = a[:n] 629 ``` 630 631 </td></tr> 632 </tbody></table> 633 634 ## Pointers to Interfaces 635 636 You almost never need a pointer to an interface. You should be passing interfaces as values—the underlying data can still be a pointer. 637 638 ### Verify Interface Compliance 639 640 Verify interface compliance at compile time *where appropriate*. This includes: 641 642 - Exported types that are required to implement specific interfaces as part of their API contract 643 - Exported or unexported types that are part of a collection of types implementing the same interface 644 - Other cases where violating an interface would break users 645 646 <table> 647 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 648 <tbody> 649 <tr><td> 650 651 ```go 652 type Handler struct { 653 // ... 654 } 655 656 657 658 func (h *Handler) ServeHTTP( 659 w http.ResponseWriter, 660 r *http.Request, 661 ) { 662 ... 663 } 664 ``` 665 666 </td><td> 667 668 ```go 669 type Handler struct { 670 // ... 671 } 672 673 var _ http.Handler = (*Handler)(nil) 674 675 func (h *Handler) ServeHTTP( 676 w http.ResponseWriter, 677 r *http.Request, 678 ) { 679 // ... 680 } 681 ``` 682 683 </td></tr> 684 </tbody></table> 685 686 The statement `var _ http.Handler = (*Handler)(nil)` will fail to compile if `*Handler` ever stops matching the `http.Handler` interface. 687 688 The right hand side of the assignment should be the zero value of the asserted type. This is `nil` for pointer types (like `*Handler`), slices, and maps, and an empty struct for struct types. 689 690 ```go 691 type LogHandler struct { 692 h http.Handler 693 log *zap.Logger 694 } 695 696 var _ http.Handler = LogHandler{} 697 698 func (h LogHandler) ServeHTTP( 699 w http.ResponseWriter, 700 r *http.Request, 701 ) { 702 // ... 703 } 704 ``` 705 706 ## Receivers and Interfaces 707 708 Methods with value receivers can be called on pointers as well as values. 709 Methods with pointer receivers can only be called on pointers or [addressable values]. 710 711 [addressable values]: https://golang.org/ref/spec#Method_values 712 713 For example, 714 715 ```go 716 type S struct { 717 data string 718 } 719 720 func (s S) Read() string { 721 return s.data 722 } 723 724 func (s *S) Write(str string) { 725 s.data = str 726 } 727 728 sVals := map[int]S{1: {"A"}} 729 730 // You can only call Read using a value 731 sVals[1].Read() 732 733 // This will not compile: 734 // sVals[1].Write("test") 735 736 sPtrs := map[int]*S{1: {"A"}} 737 738 // You can call both Read and Write using a pointer 739 sPtrs[1].Read() 740 sPtrs[1].Write("test") 741 ``` 742 743 Similarly, an interface can be satisfied by a pointer, even if the method has a value receiver. 744 745 ```go 746 type F interface { 747 f() 748 } 749 750 type S1 struct{} 751 752 func (s S1) f() {} 753 754 type S2 struct{} 755 756 func (s *S2) f() {} 757 758 s1Val := S1{} 759 s1Ptr := &S1{} 760 s2Val := S2{} 761 s2Ptr := &S2{} 762 763 var i F 764 i = s1Val 765 i = s1Ptr 766 i = s2Ptr 767 768 // The following doesn't compile, since s2Val is a value, and there is no value receiver for f. 769 // i = s2Val 770 ``` 771 772 Effective Go has a good write up on [Pointers vs. Values]. 773 774 [Pointers vs. Values]: https://golang.org/doc/effective_go.html#pointers_vs_values 775 776 ## Defer to Clean Up 777 778 Use defer to clean up resources such as files and locks. 779 780 <table> 781 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 782 <tbody> 783 <tr><td> 784 785 ```go 786 p.Lock() 787 if p.count < 10 { 788 p.Unlock() 789 return p.count 790 } 791 792 p.count++ 793 newCount := p.count 794 p.Unlock() 795 796 return newCount 797 798 // easy to miss unlocks due to multiple returns 799 ``` 800 801 </td><td> 802 803 ```go 804 p.Lock() 805 defer p.Unlock() 806 807 if p.count < 10 { 808 return p.count 809 } 810 811 p.count++ 812 return p.count 813 814 815 816 // more readable 817 ``` 818 819 </td></tr> 820 </tbody></table> 821 822 Defer has an extremely small overhead and should be avoided only if you can prove that your function execution time is in the order of nanoseconds. The readability win of using defers is worth the minuscule cost of using them. This is especially true for larger methods that have more than simple memory accesses, where the other computations are more significant than the `defer`. 823 824 Most importantly the deferred function is going to be executed even in the case of a panic, so we avoid leaving some mutex on 'lock' and potentially leaving the system in an inconsistent state. 825 826 ## Channel Size is One or None 827 828 Channels should usually have a size of one or be unbuffered. By default, channels are unbuffered and have a size of zero (blocking behaviour). Any other size must be subject to a high level of scrutiny. Consider how the size is determined, what prevents the channel from filling up under load and blocking writers, and what happens when this occurs. 829 830 <table> 831 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 832 <tbody> 833 <tr><td> 834 835 ```go 836 // Ought to be enough for anybody! 837 c := make(chan int, 64) 838 839 840 ``` 841 842 </td><td> 843 844 ```go 845 // Size of one 846 c := make(chan int, 1) // or 847 // Unbuffered channel, size of zero 848 c := make(chan int) 849 ``` 850 851 </td></tr> 852 </tbody></table> 853 854 *Note:* in some places we use buffered channels to implement a semaphore as described [here](https://robreid.io/stupid-channel-tricks-p2-semaphores/) 855 856 ## Start Enums at One 857 858 The standard way of introducing enumerations in Go is to declare a custom type and a `const` group with `iota`. Since variables have a 0 default value, you should usually start your enums on a non-zero value. 859 860 <table> 861 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 862 <tbody> 863 <tr><td> 864 865 ```go 866 type Operation int 867 868 const ( 869 Add Operation = iota 870 Subtract 871 Multiply 872 ) 873 874 // Add=0, Subtract=1, Multiply=2 875 ``` 876 877 </td><td> 878 879 ```go 880 type Operation int 881 882 const ( 883 Add Operation = iota + 1 884 Subtract 885 Multiply 886 ) 887 888 // Add=1, Subtract=2, Multiply=3 889 ``` 890 891 </td></tr> 892 </tbody></table> 893 894 *Note:* There are cases where using the zero value makes sense, for example when the zero value case is the desirable default behavior. 895 896 ```go 897 type LogOutput int 898 899 const ( 900 LogToStdout LogOutput = iota 901 LogToFile 902 LogToRemote 903 ) 904 905 // LogToStdout=0, LogToFile=1, LogToRemote=2 906 ``` 907 908 ## Use `"time"` to handle time 909 910 Time is complicated. Incorrect assumptions often made about time include the following. 911 912 1. A day has 24 hours 913 2. An hour has 60 minutes 914 3. A week has 7 days 915 4. A year has 365 days 916 5. [And a lot more](https://infiniteundo.com/post/25326999628/falsehoods-programmers-believe-about-time) 917 918 For example, *1* means that adding 24 hours to a time instant will not always yield a new calendar day. 919 920 Therefore, always use the [`"time"`] package when dealing with time because it helps deal with these incorrect assumptions in a safer, more accurate manner. 921 922 [`"time"`]: https://golang.org/pkg/time/ 923 924 ### Use `time.Time` for instants of time 925 926 Use [`time.Time`] when dealing with instants of time, and the methods on `time.Time` when comparing, adding, or subtracting time. 927 928 [`time.Time`]: https://golang.org/pkg/time/#Time 929 930 <table> 931 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 932 <tbody> 933 <tr><td> 934 935 ```go 936 func isActive(now, start, stop int) bool { 937 return start <= now && now < stop 938 } 939 ``` 940 941 </td><td> 942 943 ```go 944 func isActive(now, start, stop time.Time) bool { 945 return (start.Before(now) || start.Equal(now)) && now.Before(stop) 946 } 947 ``` 948 949 </td></tr> 950 </tbody></table> 951 952 ### Use `time.Duration` for periods of time 953 954 Use [`time.Duration`] when dealing with periods of time. 955 956 [`time.Duration`]: https://golang.org/pkg/time/#Duration 957 958 <table> 959 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 960 <tbody> 961 <tr><td> 962 963 ```go 964 func poll(delay int) { 965 for { 966 // ... 967 time.Sleep(time.Duration(delay) * time.Millisecond) 968 } 969 } 970 971 poll(10) // was it seconds or milliseconds? 972 ``` 973 974 </td><td> 975 976 ```go 977 func poll(delay time.Duration) { 978 for { 979 // ... 980 time.Sleep(delay) 981 } 982 } 983 984 poll(10*time.Second) 985 ``` 986 987 </td></tr> 988 </tbody></table> 989 990 Going back to the example of adding 24 hours to a time instant, the method we use to add time depends on intent. If we want the same time of the day, but on the next calendar day, we should use [`Time.AddDate`]. However, if we want an instant of time guaranteed to be 24 hours after the previous time, we should use [`Time.Add`]. 991 992 [`Time.AddDate`]: https://golang.org/pkg/time/#Time.AddDate 993 [`Time.Add`]: https://golang.org/pkg/time/#Time.Add 994 995 ```go 996 newDay := t.AddDate(0 /* years */, 0 /* months */, 1 /* days */) 997 maybeNewDay := t.Add(24 * time.Hour) 998 ``` 999 1000 ### Use `time.Time` and `time.Duration` with external systems 1001 1002 Use `time.Duration` and `time.Time` in interactions with external systems when possible. For example: 1003 1004 - Command-line flags: [`flag`] supports `time.Duration` via [`time.ParseDuration`] 1005 - JSON: [`encoding/json`] supports encoding `time.Time` as an [RFC 3339] string via its [`UnmarshalJSON` method] 1006 - SQL: [`database/sql`] supports converting `DATETIME` or `TIMESTAMP` columns into `time.Time` and back if the underlying driver supports it 1007 - YAML: [`gopkg.in/yaml.v2`] supports `time.Time` as an [RFC 3339] string, and `time.Duration` via [`time.ParseDuration`]. 1008 1009 [`flag`]: https://golang.org/pkg/flag/ 1010 [`time.ParseDuration`]: https://golang.org/pkg/time/#ParseDuration 1011 [`encoding/json`]: https://golang.org/pkg/encoding/json/ 1012 [RFC 3339]: https://tools.ietf.org/html/rfc3339 1013 [`UnmarshalJSON` method]: https://golang.org/pkg/time/#Time.UnmarshalJSON 1014 [`database/sql`]: https://golang.org/pkg/database/sql/ 1015 [`gopkg.in/yaml.v2`]: https://godoc.org/gopkg.in/yaml.v2 1016 1017 When it is not possible to use `time.Duration` in these interactions, use `int` or `float64` and include the unit in the name of the field. For example, since `encoding/json` does not support `time.Duration`, the unit is included in the name of the field. 1018 1019 <table> 1020 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1021 <tbody> 1022 <tr><td> 1023 1024 ```go 1025 // {"interval": 2} 1026 type Config struct { 1027 Interval int `json:"interval"` 1028 } 1029 ``` 1030 1031 </td><td> 1032 1033 ```go 1034 // {"intervalMillis": 2000} 1035 type Config struct { 1036 IntervalMillis int `json:"intervalMillis"` 1037 } 1038 ``` 1039 1040 </td></tr> 1041 </tbody></table> 1042 1043 When it is not possible to use `time.Time` in these interactions, unless an alternative is agreed upon, use `string` and format timestamps as defined in [RFC 3339]. This format is used by default by [`Time.nmarshalText`] and is available for use in `Time.Format` and `time.Parse` via [`time.RFC3339`]. 1044 1045 [`Time.UnmarshalText`]: https://golang.org/pkg/time/#Time.UnmarshalText 1046 [`time.RFC3339`]: https://golang.org/pkg/time/#RFC3339 1047 1048 Although this tends to not be a problem in practice, keep in mind that the `"time"` package does not support parsing timestamps with leap seconds ([8728]), nor does it account for leap seconds in calculations ([15190]). If you compare two instants of time, the difference will not include the leap seconds that may have occurred between those two instants. 1049 1050 [8728]: https://github.com/golang/go/issues/8728 1051 [15190]: https://github.com/golang/go/issues/15190 1052 1053 ## Error Types 1054 1055 There are various options for declaring errors: 1056 1057 - [`errors.New`] for errors with simple static strings 1058 - [`fmt.Errorf`] for formatted error strings 1059 - Custom types that implement an `Error()` method 1060 - Wrapped errors using [`"pkg/errors".Wrap`] 1061 1062 When returning errors, consider the following to determine the best choice: 1063 1064 - Is this a simple error that needs no extra information? If so, [`errors.New`] should suffice. 1065 - Do the clients need to detect and handle this error? If so, you should use a custom type, and implement the `Error()` method. 1066 - Are you propagating an error returned by a downstream function? If so, check the [section on error wrapping](#error-wrapping). 1067 - Otherwise, [`fmt.Errorf`] is okay. 1068 1069 [`errors.New`]: https://golang.org/pkg/errors/#New 1070 [`fmt.Errorf`]: https://golang.org/pkg/fmt/#Errorf 1071 [`"pkg/errors".Wrap`]: https://godoc.org/github.com/pkg/errors#Wrap 1072 1073 If the client needs to detect the error, and you have created a simple error 1074 using [`errors.New`], use a var for the error. 1075 1076 <table> 1077 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1078 <tbody> 1079 <tr><td> 1080 1081 ```go 1082 // package foo 1083 1084 func Open() error { 1085 return errors.New("could not open") 1086 } 1087 1088 // package bar 1089 1090 func use() { 1091 if err := foo.Open(); err != nil { 1092 if err.Error() == "could not open" { 1093 // handle 1094 } else { 1095 panic("unknown error") 1096 } 1097 } 1098 } 1099 ``` 1100 1101 </td><td> 1102 1103 ```go 1104 // package foo 1105 1106 var ErrCouldNotOpen = errors.New("could not open") 1107 1108 func Open() error { 1109 return ErrCouldNotOpen 1110 } 1111 1112 // package bar 1113 1114 if err := foo.Open(); err != nil { 1115 if errors.Is(err, foo.ErrCouldNotOpen) { 1116 // handle 1117 } else { 1118 panic("unknown error") 1119 } 1120 } 1121 ``` 1122 1123 </td></tr> 1124 </tbody></table> 1125 1126 If you have an error that clients may need to detect, and you would like to add more information to it (e.g., it is not a static string), then you should use a custom type. 1127 1128 <table> 1129 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1130 <tbody> 1131 <tr><td> 1132 1133 ```go 1134 func open(file string) error { 1135 return fmt.Errorf("file %q not found", file) 1136 } 1137 1138 func use() { 1139 if err := open("testfile.txt"); err != nil { 1140 if strings.Contains(err.Error(), "not found") { 1141 // handle 1142 } else { 1143 panic("unknown error") 1144 } 1145 } 1146 } 1147 1148 1149 1150 1151 1152 1153 1154 1155 ``` 1156 1157 </td><td> 1158 1159 ```go 1160 type errNotFound struct { 1161 file string 1162 } 1163 1164 func (e errNotFound) Error() string { 1165 return fmt.Sprintf("file %q not found", e.file) 1166 } 1167 1168 func open(file string) error { 1169 return errNotFound{file: file} 1170 } 1171 1172 func use() { 1173 if err := open("testfile.txt"); err != nil { 1174 if _, ok := err.(errNotFound); ok { 1175 // handle 1176 } else { 1177 panic("unknown error") 1178 } 1179 } 1180 } 1181 ``` 1182 1183 </td></tr> 1184 </tbody></table> 1185 1186 Be careful with exporting custom error types directly since they become part of the public API of the package. It is preferable to expose matcher functions to check the error instead. 1187 1188 ```go 1189 // package foo 1190 1191 type errNotFound struct { 1192 file string 1193 } 1194 1195 func (e errNotFound) Error() string { 1196 return fmt.Sprintf("file %q not found", e.file) 1197 } 1198 1199 func IsNotFoundError(err error) bool { 1200 return errors.Is(err, errNotFound) 1201 } 1202 1203 func Open(file string) error { 1204 return errNotFound{file: file} 1205 } 1206 1207 // package bar 1208 1209 if err := foo.Open("foo"); err != nil { 1210 if foo.IsNotFoundError(err) { 1211 // handle 1212 } else { 1213 panic("unknown error") 1214 } 1215 } 1216 ``` 1217 1218 ### Error Wrapping 1219 1220 There are three main options for propagating errors if a call fails: 1221 1222 - Return the original error if there is no additional context to add and you want to maintain the original error type. 1223 - Use [`fmt.Errorf`] if the callers do not need to detect or handle that specific error case. 1224 - Use a custom type where you can detail the failure reason. 1225 1226 It is recommended to add context where possible so that instead of a vague error such as "connection refused", you get more useful errors such as "call service foo: connection refused". When adding context to returned errors, keep the context succinct by avoiding phrases like "failed to", which state the obvious and pile up as the error percolates up through the stack: 1227 1228 <table> 1229 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1230 <tbody> 1231 <tr><td> 1232 1233 ```go 1234 s, err := store.New() 1235 if err != nil { 1236 return fmt.Errorf("failed to create new store: %v", err) 1237 } 1238 ``` 1239 1240 </td><td> 1241 1242 ```go 1243 s, err := store.New() 1244 if err != nil { 1245 return fmt.Errorf("new store: %v", err) 1246 } 1247 ``` 1248 1249 <tr><td> 1250 1251 ``` 1252 failed to x: failed to y: failed to create new store: the error 1253 ``` 1254 1255 </td><td> 1256 1257 ``` 1258 x: y: new store: the error 1259 1260 ``` 1261 1262 </td></tr> 1263 </tbody></table> 1264 1265 However once the error is sent to another system, it should be clear the message is an error (e.g. an `err` tag or "Failed" prefix in logs). 1266 1267 See also [Don't just check errors, handle them gracefully]. 1268 1269 [`"pkg/errors".Cause`]: https://godoc.org/github.com/pkg/errors#Cause 1270 [Don't just check errors, handle them gracefully]: https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully 1271 1272 ### Handle Type Assertion Failures 1273 1274 The single return value form of a [type assertion] will panic on an incorrect type. Therefore, always use the "comma ok" idiom. 1275 1276 [type assertion]: https://golang.org/ref/spec#Type_assertions 1277 1278 <table> 1279 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1280 <tbody> 1281 <tr><td> 1282 1283 ```go 1284 t := i.(string) 1285 1286 1287 1288 ``` 1289 1290 </td><td> 1291 1292 ```go 1293 t, ok := i.(string) 1294 if !ok { 1295 // handle the error gracefully 1296 } 1297 ``` 1298 1299 </td></tr> 1300 </tbody></table> 1301 1302 ### Panic with care 1303 1304 Code running in production must avoid panics. Panics are a major source of [cascading failures]. If an error occurs, the function must return an error and allow the caller to decide how to handle it. 1305 1306 [cascading failures]: https://en.wikipedia.org/wiki/Cascading_failure 1307 1308 <table> 1309 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1310 <tbody> 1311 <tr><td> 1312 1313 ```go 1314 func run(args []string) { 1315 if len(args) == 0 { 1316 panic("an argument is required") 1317 } 1318 // ... 1319 } 1320 1321 func main() { 1322 run(os.Args[1:]) 1323 } 1324 1325 1326 1327 1328 ``` 1329 1330 </td><td> 1331 1332 ```go 1333 func run(args []string) error { 1334 if len(args) == 0 { 1335 return errors.New("an argument is required") 1336 } 1337 // ... 1338 return nil 1339 } 1340 1341 func main() { 1342 if err := run(os.Args[1:]); err != nil { 1343 fmt.Fprintln(os.Stderr, err) 1344 os.Exit(1) 1345 } 1346 } 1347 ``` 1348 1349 </td></tr> 1350 </tbody></table> 1351 1352 Panic/recover is not an error handling strategy. A program must panic only when something irrecoverable happens such as a nil dereference. An exception to this is program initialization: bad things at program startup that should abort the program may cause panic. 1353 1354 ```go 1355 var _statusTemplate = template.Must(template.New("name").Parse("_statusHTML")) 1356 ``` 1357 1358 Even in tests, prefer `t.Fatal` or `t.FailNow` over panics to ensure that the test is marked as failed. 1359 1360 <table> 1361 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1362 <tbody> 1363 <tr><td> 1364 1365 ```go 1366 // func TestFoo(t *testing.T) 1367 1368 f, err := os.CreateTemp("", "test") 1369 if err != nil { 1370 panic("failed to set up test") 1371 } 1372 ``` 1373 1374 </td><td> 1375 1376 ```go 1377 // func TestFoo(t *testing.T) 1378 1379 f, err := os.CreateTemp("", "test") 1380 if err != nil { 1381 t.Fatal("failed to set up test") 1382 } 1383 ``` 1384 1385 </td></tr> 1386 </tbody></table> 1387 1388 ## Avoid Mutable Globals 1389 1390 Avoid mutating global variables, instead opting for dependency injection. This applies to function pointers as well as other kinds of values. 1391 1392 <table> 1393 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1394 <tbody> 1395 <tr><td> 1396 1397 ```go 1398 // sign.go 1399 1400 var _timeNow = time.Now 1401 1402 func sign(msg string) string { 1403 now := _timeNow() 1404 return signWithTime(msg, now) 1405 } 1406 1407 1408 1409 1410 1411 1412 1413 1414 ``` 1415 1416 </td><td> 1417 1418 ```go 1419 // sign.go 1420 1421 type signer struct { 1422 now func() time.Time 1423 } 1424 1425 func newSigner() *signer { 1426 return &signer{ 1427 now: time.Now, 1428 } 1429 } 1430 1431 func (s *signer) Sign(msg string) string { 1432 now := s.now() 1433 return signWithTime(msg, now) 1434 } 1435 ``` 1436 </td></tr> 1437 <tr><td> 1438 1439 ```go 1440 // sign_test.go 1441 1442 func TestSign(t *testing.T) { 1443 oldTimeNow := _timeNow 1444 _timeNow = func() time.Time { 1445 return someFixedTime 1446 } 1447 defer func() { _timeNow = oldTimeNow }() 1448 1449 assert.Equal(t, want, sign(give)) 1450 } 1451 ``` 1452 1453 </td><td> 1454 1455 ```go 1456 // sign_test.go 1457 1458 func TestSigner(t *testing.T) { 1459 s := newSigner() 1460 s.now = func() time.Time { 1461 return someFixedTime 1462 } 1463 1464 assert.Equal(t, want, s.Sign(give)) 1465 } 1466 1467 ``` 1468 1469 </td></tr> 1470 </tbody></table> 1471 1472 ## Avoid Embedding Types in Public Structs 1473 1474 These embedded types leak implementation details, inhibit type evolution, and obscure documentation. Assuming you have implemented a variety of list types using a shared `AbstractList`, avoid embedding the `AbstractList` in your concrete list implementations. Instead, hand-write only the methods to your concrete list that will delegate to the abstract list. 1475 1476 ```go 1477 type AbstractList struct {} 1478 1479 // Add adds an entity to the list. 1480 func (l *AbstractList) Add(e Entity) { 1481 // ... 1482 } 1483 1484 // Remove removes an entity from the list. 1485 func (l *AbstractList) Remove(e Entity) { 1486 // ... 1487 } 1488 ``` 1489 1490 <table> 1491 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1492 <tbody> 1493 <tr><td> 1494 1495 ```go 1496 // ConcreteList is a list of entities. 1497 type ConcreteList struct { 1498 *AbstractList 1499 } 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 ``` 1511 1512 </td><td> 1513 1514 ```go 1515 // ConcreteList is a list of entities. 1516 type ConcreteList struct { 1517 list *AbstractList 1518 } 1519 1520 // Add adds an entity to the list. 1521 func (l *ConcreteList) Add(e Entity) { 1522 l.list.Add(e) 1523 } 1524 1525 // Remove removes an entity from the list. 1526 func (l *ConcreteList) Remove(e Entity) { 1527 l.list.Remove(e) 1528 } 1529 ``` 1530 1531 </td></tr> 1532 </tbody></table> 1533 1534 Go allows [type embedding] as a compromise between inheritance and composition. The outer type gets implicit copies of the embedded type's methods. These methods, by default, delegate to the same method of the embedded instance. 1535 1536 [type embedding]: https://golang.org/doc/effective_go.html#embedding 1537 1538 The struct also gains a field by the same name as the type. So, if the embedded type is public, the field is public. To maintain backward compatibility, every future version of the outer type must keep the embedded type. 1539 1540 An embedded type is rarely necessary. It is a convenience that helps you avoid writing tedious delegate methods. 1541 1542 Even embedding a compatible AbstractList *interface*, instead of the struct, would offer the developer more flexibility to change in the future, but still leak the detail that the concrete lists use an abstract implementation. 1543 1544 <table> 1545 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1546 <tbody> 1547 <tr><td> 1548 1549 ```go 1550 // AbstractList is a generalized implementation 1551 // for various kinds of lists of entities. 1552 type AbstractList interface { 1553 Add(Entity) 1554 Remove(Entity) 1555 } 1556 1557 // ConcreteList is a list of entities. 1558 type ConcreteList struct { 1559 AbstractList 1560 } 1561 1562 1563 1564 1565 ``` 1566 1567 </td><td> 1568 1569 ```go 1570 // ConcreteList is a list of entities. 1571 type ConcreteList struct { 1572 list AbstractList 1573 } 1574 1575 // Add adds an entity to the list. 1576 func (l *ConcreteList) Add(e Entity) { 1577 l.list.Add(e) 1578 } 1579 1580 // Remove removes an entity from the list. 1581 func (l *ConcreteList) Remove(e Entity) { 1582 l.list.Remove(e) 1583 } 1584 ``` 1585 1586 </td></tr> 1587 </tbody></table> 1588 1589 Either with an embedded struct or an embedded interface, the embedded type places limits on the evolution of the type. 1590 1591 - Adding methods to an embedded interface is a breaking change. 1592 - Removing methods from an embedded struct is a breaking change. 1593 - Removing the embedded type is a breaking change. 1594 - Replacing the embedded type, even with an alternative that satisfies the same 1595 interface, is a breaking change. 1596 1597 Although writing these delegate methods is tedious, the additional effort hides an implementation detail, leaves more opportunities for change and also eliminates indirection for discovering the full List interface in documentation. 1598 1599 ## Avoid Using Built-In Names 1600 1601 The Go [language specification] outlines several built-in, [predeclared identifiers] that should not be used as names within Go programs. 1602 1603 Depending on context, reusing these identifiers as names will either shadow the original within the current lexical scope (and any nested scopes) or make affected code confusing. In the best case, the compiler will complain; in the worst case, such code may introduce latent, hard-to-grep bugs. 1604 1605 [language specification]: https://golang.org/ref/spec 1606 [predeclared identifiers]: https://golang.org/ref/spec#Predeclared_identifiers 1607 1608 <table> 1609 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1610 <tbody> 1611 <tr><td> 1612 1613 ```go 1614 var error string 1615 // `error` shadows the builtin 1616 1617 // or 1618 1619 func handleErrorMessage(error string) { 1620 // `error` shadows the builtin 1621 } 1622 ``` 1623 1624 </td><td> 1625 1626 ```go 1627 var errorMessage string 1628 // `error` refers to the builtin 1629 1630 // or 1631 1632 func handleErrorMessage(msg string) { 1633 // `error` refers to the builtin 1634 } 1635 ``` 1636 1637 </td></tr> 1638 <tr><td> 1639 1640 ```go 1641 type Foo struct { 1642 // While these fields technically don't 1643 // constitute shadowing, grepping for 1644 // `error` or `string` strings is now 1645 // ambiguous. 1646 error error 1647 string string 1648 } 1649 1650 func (f Foo) Error() error { 1651 // `error` and `f.error` are 1652 // visually similar 1653 return f.error 1654 } 1655 1656 func (f Foo) String() string { 1657 // `string` and `f.string` are 1658 // visually similar 1659 return f.string 1660 } 1661 ``` 1662 1663 </td><td> 1664 1665 ```go 1666 type Foo struct { 1667 // `error` and `string` strings are 1668 // now unambiguous. 1669 err error 1670 str string 1671 } 1672 1673 func (f Foo) Error() error { 1674 return f.err 1675 } 1676 1677 func (f Foo) String() string { 1678 return f.str 1679 } 1680 1681 1682 1683 1684 1685 1686 ``` 1687 </td></tr> 1688 </tbody></table> 1689 1690 *Note:* the compiler will not generate errors when using predeclared identifiers, but tools such as `go vet` should correctly point out these and other cases of shadowing. 1691 1692 ## Avoid `init()` 1693 1694 Avoid `init()` where possible. When `init()` is unavoidable or desirable, code should attempt to: 1695 1696 1. Be completely deterministic, regardless of program environment or invocation. 1697 2. Avoid depending on the ordering or side-effects of other `init()` functions. While `init()` ordering is well-known, code can change, and thus relationships between `init()` functions can make code brittle and error-prone. 1698 3. Avoid accessing or manipulating global or environment state, such as machine information, environment variables, working directory, program arguments/inputs, etc. 1699 4. Avoid I/O, including both filesystem, network, and system calls. 1700 1701 Code that cannot satisfy these requirements likely belongs as a helper to be called as part of `main()` (or elsewhere in a program's lifecycle), or be written as part of `main()` itself. In particular, libraries that are intended to be used by other programs should take special care to be completely deterministic and not perform "init magic". 1702 1703 <table> 1704 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1705 <tbody> 1706 <tr><td> 1707 1708 ```go 1709 type Foo struct { 1710 // ... 1711 } 1712 1713 var _defaultFoo Foo 1714 1715 func init() { 1716 _defaultFoo = Foo{ 1717 // ... 1718 } 1719 } 1720 1721 1722 ``` 1723 1724 </td><td> 1725 1726 ```go 1727 var _defaultFoo = Foo{ 1728 // ... 1729 } 1730 1731 // or, better, for testability: 1732 1733 var _defaultFoo = defaultFoo() 1734 1735 func defaultFoo() Foo { 1736 return Foo{ 1737 // ... 1738 } 1739 } 1740 ``` 1741 1742 </td></tr> 1743 <tr><td> 1744 1745 ```go 1746 type Config struct { 1747 // ... 1748 } 1749 1750 var _config Config 1751 1752 func init() { 1753 // Bad: based on current directory 1754 cwd, _ := os.Getwd() 1755 1756 // Bad: I/O 1757 raw, _ := os.ReadFile( 1758 path.Join(cwd, "config", "config.yaml"), 1759 ) 1760 1761 yaml.Unmarshal(raw, &_config) 1762 } 1763 ``` 1764 1765 </td><td> 1766 1767 ```go 1768 type Config struct { 1769 // ... 1770 } 1771 1772 func loadConfig() Config { 1773 cwd, err := os.Getwd() 1774 // handle err 1775 1776 raw, err := os.ReadFile( 1777 path.Join(cwd, "config", "config.yaml"), 1778 ) 1779 // handle err 1780 1781 var config Config 1782 yaml.Unmarshal(raw, &config) 1783 1784 return config 1785 } 1786 ``` 1787 1788 </td></tr> 1789 </tbody></table> 1790 1791 Considering the above, some situations in which `init()` may be preferable or necessary might include: 1792 1793 - Complex expressions that cannot be represented as single assignments. 1794 - Pluggable hooks, such as `database/sql` dialects, encoding type registries, etc. 1795 - Optimizations to [Google Cloud Functions] and other forms of deterministic precomputation. 1796 1797 [Google Cloud Functions]: https://cloud.google.com/functions/docs/bestpractices/tips#use_global_variables_to_reuse_objects_in_future_invocations 1798 1799 ## Exit in Main 1800 1801 Go programs use [`os.Exit`] or [`log.Fatal*`] to exit immediately. (Panicking is not a good way to exit programs, please [Panic with care](#panic-with-care).) 1802 1803 [`os.Exit`]: https://golang.org/pkg/os/#Exit 1804 [`log.Fatal*`]: https://golang.org/pkg/log/#Fatal 1805 1806 Call one of `os.Exit` or `log.Fatal*` **only in `main()`**. All other functions should return errors to signal failure. 1807 1808 <table> 1809 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1810 <tbody> 1811 <tr><td> 1812 1813 ```go 1814 func main() { 1815 body := readFile(path) 1816 fmt.Println(body) 1817 } 1818 1819 func readFile(path string) string { 1820 f, err := os.Open(path) 1821 if err != nil { 1822 log.Fatal(err) 1823 } 1824 1825 b, err := io.ReadAll(f) 1826 if err != nil { 1827 log.Fatal(err) 1828 } 1829 1830 return string(b) 1831 } 1832 1833 1834 1835 ``` 1836 1837 </td><td> 1838 1839 ```go 1840 func main() { 1841 body, err := readFile(path) 1842 if err != nil { 1843 log.Fatal(err) 1844 } 1845 fmt.Println(body) 1846 } 1847 1848 func readFile(path string) (string, error) { 1849 f, err := os.Open(path) 1850 if err != nil { 1851 return "", err 1852 } 1853 1854 b, err := io.ReadAll(f) 1855 if err != nil { 1856 return "", err 1857 } 1858 1859 return string(b), nil 1860 } 1861 ``` 1862 1863 </td></tr> 1864 </tbody></table> 1865 1866 Rationale: Programs with multiple functions that exit present a few issues: 1867 1868 - Non-obvious control flow: Any function can exit the program so it becomes difficult to reason about the control flow. 1869 - Difficult to test: A function that exits the program will also exit the test calling it. This makes the function difficult to test and introduces risk of skipping other tests that have not yet been run by `go test`. 1870 - Skipped cleanup: When a function exits the program, it skips function calls enqueued with `defer` statements. This adds risk of skipping important cleanup tasks. 1871 1872 ### Exit Once 1873 1874 If possible, prefer to call `os.Exit` or `log.Fatal` **at most once** in your `main()`. If there are multiple error scenarios that halt program execution, put that logic under a separate function and return errors from it. 1875 1876 This has the effect of shortening your `main()` function and putting all key business logic into a separate, testable function. 1877 1878 <table> 1879 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1880 <tbody> 1881 <tr><td> 1882 1883 ```go 1884 package main 1885 1886 func main() { 1887 args := os.Args[1:] 1888 if len(args) != 1 { 1889 log.Fatal("missing file") 1890 } 1891 name := args[0] 1892 1893 f, err := os.Open(name) 1894 if err != nil { 1895 log.Fatal(err) 1896 } 1897 defer f.Close() 1898 1899 // If we call log.Fatal after this line, 1900 // f.Close will not be called. 1901 1902 b, err := io.ReadAll(f) 1903 if err != nil { 1904 log.Fatal(err) 1905 } 1906 1907 // ... 1908 } 1909 1910 1911 1912 ``` 1913 1914 </td><td> 1915 1916 ```go 1917 package main 1918 1919 func main() { 1920 if err := run(); err != nil { 1921 log.Fatal(err) 1922 } 1923 } 1924 1925 func run() error { 1926 args := os.Args[1:] 1927 if len(args) != 1 { 1928 return errors.New("missing file") 1929 } 1930 name := args[0] 1931 1932 f, err := os.Open(name) 1933 if err != nil { 1934 return err 1935 } 1936 defer f.Close() 1937 1938 b, err := io.ReadAll(f) 1939 if err != nil { 1940 return err 1941 } 1942 1943 // ... 1944 } 1945 ``` 1946 1947 </td></tr> 1948 </tbody></table> 1949 1950 ## Performance 1951 1952 Performance-specific guidelines apply only to the hot path. 1953 1954 ### Prefer strconv over fmt 1955 1956 When converting primitives to/from strings, `strconv` is faster than `fmt`. 1957 1958 <table> 1959 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1960 <tbody> 1961 <tr><td> 1962 1963 ```go 1964 for i := 0; i < b.N; i++ { 1965 s := fmt.Sprint(rand.Int()) 1966 } 1967 ``` 1968 1969 </td><td> 1970 1971 ```go 1972 for i := 0; i < b.N; i++ { 1973 s := strconv.Itoa(rand.Int()) 1974 } 1975 ``` 1976 1977 </td></tr> 1978 <tr><td> 1979 1980 ``` 1981 BenchmarkFmtSprint-4 143 ns/op 2 allocs/op 1982 ``` 1983 1984 </td><td> 1985 1986 ``` 1987 BenchmarkStrconv-4 64.2 ns/op 1 allocs/op 1988 ``` 1989 1990 </td></tr> 1991 </tbody></table> 1992 1993 ### Avoid string-to-byte conversion 1994 1995 Do not create byte slices from a fixed string repeatedly. Instead, perform the conversion once and capture the result. 1996 1997 <table> 1998 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 1999 <tbody> 2000 <tr><td> 2001 2002 ```go 2003 for i := 0; i < b.N; i++ { 2004 w.Write([]byte("Hello world")) 2005 } 2006 2007 ``` 2008 2009 </td><td> 2010 2011 ```go 2012 data := []byte("Hello world") 2013 for i := 0; i < b.N; i++ { 2014 w.Write(data) 2015 } 2016 ``` 2017 2018 </tr> 2019 <tr><td> 2020 2021 ``` 2022 BenchmarkBad-4 50000000 22.2 ns/op 2023 ``` 2024 2025 </td><td> 2026 2027 ``` 2028 BenchmarkGood-4 500000000 3.25 ns/op 2029 ``` 2030 2031 </td></tr> 2032 </tbody></table> 2033 2034 ### Prefer Specifying Container Capacity 2035 2036 Specify container capacity where possible in order to allocate memory for the container up front. This minimizes subsequent allocations (by copying and resizing of the container) as elements are added. 2037 2038 ### Specifying Map Capacity Hints 2039 2040 Where possible, provide capacity hints when initializing maps with `make()`. 2041 2042 ```go 2043 make(map[T1]T2, hint) 2044 ``` 2045 2046 Providing a capacity hint to `make()` tries to right-size the map at initialization time, which reduces the need for growing the map and allocations as elements are added to the map. 2047 2048 *Note:* unlike slices, map capacity hints do not guarantee complete, preemptive allocation, but are used to approximate the number of hashmap buckets required. Consequently, allocations may still occur when adding elements to the map, even up to the specified capacity. 2049 2050 <table> 2051 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 2052 <tbody> 2053 <tr><td> 2054 2055 ```go 2056 m := make(map[string]os.FileInfo) 2057 2058 files, _ := os.ReadDir("./files") 2059 for _, f := range files { 2060 m[f.Name()] = f 2061 } 2062 2063 ``` 2064 2065 </td><td> 2066 2067 ```go 2068 2069 files, _ := os.ReadDir("./files") 2070 2071 m := make(map[string]os.FileInfo, len(files)) 2072 for _, f := range files { 2073 m[f.Name()] = f 2074 } 2075 ``` 2076 2077 </td></tr> 2078 <tr><td> 2079 2080 `m` is created without a size hint; there may be more allocations at assignment time. 2081 2082 </td><td> 2083 2084 `m` is created with a size hint; there may be fewer allocations at assignment time. 2085 2086 </td></tr> 2087 </tbody></table> 2088 2089 ### Specifying Slice Capacity 2090 2091 Where possible, provide capacity hints when initializing slices with `make()`, particularly when appending. 2092 2093 ```go 2094 make([]T, length, capacity) 2095 ``` 2096 2097 Unlike maps, slice capacity is not a hint: the compiler will allocate enough memory for the capacity of the slice as provided to `make()`, which means that subsequent `append()` operations will incur zero allocations (until the length of the slice matches the capacity, after which any appends will require a resize to hold additional elements). 2098 2099 <table> 2100 <thead><tr><th>Bad</th><th>Good</th></tr></thead> 2101 <tbody> 2102 <tr><td> 2103 2104 ```go 2105 for n := 0; n < b.N; n++ { 2106 data := make([]int, 0) 2107 for k := 0; k < size; k++{ 2108 data = append(data, k) 2109 } 2110 } 2111 ``` 2112 2113 </td><td> 2114 2115 ```go 2116 for n := 0; n < b.N; n++ { 2117 data := make([]int, 0, size) 2118 for k := 0; k < size; k++{ 2119 data = append(data, k) 2120 } 2121 } 2122 ``` 2123 2124 </td></tr> 2125 <tr><td> 2126 2127 ``` 2128 BenchmarkBad-4 100000000 2.48s 2129 ``` 2130 2131 </td><td> 2132 2133 ``` 2134 BenchmarkGood-4 100000000 0.21s 2135 ``` 2136 2137 </td></tr> 2138 </tbody></table>