For the Love of Go 1.23 (John Arundel) (Z-Library)
Author: John Arundel
商业
No Description
📄 File Format:
PDF
💾 File Size:
1.8 MB
12
Views
0
Downloads
0.00
Total Donations
📄 Text Preview (First 20 pages)
ℹ️
Registered users can read the full content for free
Register as a Gaohf Library member to read the complete e-book online for free and enjoy a better reading experience.
📄 Page
1
(This page has no text content)
📄 Page
2
Contents Praise for For the Love of Go 9 Introduction 10 What’s this? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 What you’ll need . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10 Where to find the code examples . . . . . . . . . . . . . . . . . . . . . . . . 11 What you’ll learn . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 The love of Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 Programming with confidence . . . . . . . . . . . . . . . . . . . . . . . . . 12 What makes this book different? . . . . . . . . . . . . . . . . . . . . . . . . 13 How to use this book . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 1. Testing times 14 Creating a new project . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 Creating Go files . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 Running the tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 Formatting code with gofmt . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Fixing format with gofmt -w . . . . . . . . . . . . . . . . . . . . . . . . . 18 Functions in Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 A failing test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19 The testing package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21 The signature of test functions . . . . . . . . . . . . . . . . . . . . . . . . . 22 The test body . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22 The function under test . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23 if statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Conditional expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 2. Go forth andmultiply 27 Designing a new feature, guided by tests . . . . . . . . . . . . . . . . . . . . 27 Testing a null implementation . . . . . . . . . . . . . . . . . . . . . . . . . 28 Writing the real implementation . . . . . . . . . . . . . . . . . . . . . . . . 30 Introducing test cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 A slice of test cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31 Looping over test cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32 A different kind of problem: division . . . . . . . . . . . . . . . . . . . . . . 33 Error values in Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33 Test behaviours, not functions . . . . . . . . . . . . . . . . . . . . . . . . . 34 Testing valid input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35 2
📄 Page
3
Receiving the error value . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 37 3. Errors and expectations 39 Returning an error value . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39 Testing the invalid input case . . . . . . . . . . . . . . . . . . . . . . . . . . 41 Detecting invalid input . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 Constructing error values . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 The structure of tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 The development process . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 Comparing floating‐point values . . . . . . . . . . . . . . . . . . . . . . . . 43 A Sqrt function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Running programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 The main package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 The go run command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 The go build command . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 4. Happy Fun Books 50 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50 Variables and values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 Type checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 More types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 Zero values and default values . . . . . . . . . . . . . . . . . . . . . . . . . 54 Introducing structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 Type definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Exported identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 The core package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 The very first test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 Testing the core struct type . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Struct literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Assigning a struct literal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 The unfailable test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 5. Story time 61 User stories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 What are the core stories? . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 The first story . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 Struct variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 The short declaration form . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Referencing struct fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Writing the test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Getting to a failing test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Assigning to struct fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Implementing Buy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 3
📄 Page
4
Test coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Test‐last development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Uncovering a problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 “Covered” versus “tested” . . . . . . . . . . . . . . . . . . . . . . . . . . . 70 Juking the stats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 6. Slicing & dicing 73 Slices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Slice variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 Slice literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Slice indexes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Slice length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74 Modifying slice elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Appending to slices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 A collection of books . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Setting up the world . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Comparing slices (and other things) . . . . . . . . . . . . . . . . . . . . . . 76 Unique identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 Getting to a failing test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79 Finding a book by ID . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Crime doesn’t pay . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 7. Mapmischief 85 Introducing the map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 Adding a new element to a map . . . . . . . . . . . . . . . . . . . . . . . . 87 Accessing element fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 Updating elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 Non‐existent keys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88 Checking if a value is in the map . . . . . . . . . . . . . . . . . . . . . . . . 89 Returning an error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 Testing the invalid input case . . . . . . . . . . . . . . . . . . . . . . . . . . 90 Updating GetAllBooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91 Sorting slices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Function literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 8. Objects behaving badly 98 Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98 Doing computation with objects . . . . . . . . . . . . . . . . . . . . . . . . 99 Testing NetPriceCents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 Defining methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 Methods on non‐local types . . . . . . . . . . . . . . . . . . . . . . . . . . 102 Creating custom types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 4
📄 Page
5
More types and methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104 Creating a Catalog type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 9. Wrapper’s delight 109 The strings.Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109 Creating a type based on strings.Builder . . . . . . . . . . . . . . . . . 111 Wrapping strings.Builder with a struct . . . . . . . . . . . . . . . . . . 112 A puzzle about function parameters . . . . . . . . . . . . . . . . . . . . . . 114 Parameters are passed by value . . . . . . . . . . . . . . . . . . . . . . . . 115 Creating a pointer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 Declaring pointer parameters . . . . . . . . . . . . . . . . . . . . . . . . . 116 What can we do with pointers? . . . . . . . . . . . . . . . . . . . . . . . . . 117 The * operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 Nil desperandum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117 Pointer methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 10. Very valid values 120 Validating methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 Automatic dereferencing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 Always valid fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 Unexported fields and cmp.Equal . . . . . . . . . . . . . . . . . . . . . . . 127 Always valid structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127 A set of valid values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130 Using a map to represent a set . . . . . . . . . . . . . . . . . . . . . . . . . 131 Defining constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 Referring to constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 Standard library constants . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 When the values don’t matter . . . . . . . . . . . . . . . . . . . . . . . . . 133 Introducing iota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 11: Opening statements 138 Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 What is assignment? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 Short variable declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . 140 Assigning more than one value . . . . . . . . . . . . . . . . . . . . . . . . . 140 The blank identifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 Increment and decrement statements . . . . . . . . . . . . . . . . . . . . . 142 if statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142 The happy path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143 else branches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 Early return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 Conditional expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 5
📄 Page
6
And, or, and not . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 bool variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 Compound if statements . . . . . . . . . . . . . . . . . . . . . . . . . . . 147 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149 12: Switch which? 150 The switch statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 Switch cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 The default case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 Switch expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 Exiting a switch case early with break . . . . . . . . . . . . . . . . . . . . . 152 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 The for keyword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 Forever loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 Using range to loop over collections . . . . . . . . . . . . . . . . . . . . . . 154 Receiving index and element values from range . . . . . . . . . . . . . . . 154 Conditional for statements . . . . . . . . . . . . . . . . . . . . . . . . . . 155 Init and post statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 range over integer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 Jumping to the next element with continue . . . . . . . . . . . . . . . . . . 157 Exiting loops with break . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 Controlling nested loops with labels . . . . . . . . . . . . . . . . . . . . . . 159 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 13: Fun with functions 161 Declaring functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 Parameter lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 Result lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 The function body . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 Calling a function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 Using result values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 Return statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 Function values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 Function literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167 Closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 Cleaning up resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 The defer keyword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 Multiple defers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 Named result parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 Naked returns considered harmful . . . . . . . . . . . . . . . . . . . . . . . 171 Modifying result parameters after exit . . . . . . . . . . . . . . . . . . . . . 172 Deferring a function literal . . . . . . . . . . . . . . . . . . . . . . . . . . . 172 Deferring a closure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 Variadic functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 6
📄 Page
7
14: Building blocks 177 Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 The main package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 The main function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 The init function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 Alternatives to init . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 Building an executable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 The executable binary file . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 Cross‐compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 Exiting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 15. The Tao of Go 184 Kindness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 Simplicity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 Humility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 Not striving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 The love of Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 About this book 189 Who wrote this? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 Free updates to future editions . . . . . . . . . . . . . . . . . . . . . . . . . 190 Join my Go Club . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 Video course . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190 The Power of Go: Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 The Power of Go: Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 Know Go: Generics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 Further reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 Credits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 Acknowledgements 193 A sneak preview 194 1. Packages 195 The universal library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 Packages are a force multiplier . . . . . . . . . . . . . . . . . . . . . . 196 The universal Go library is huge . . . . . . . . . . . . . . . . . . . . . 196 Sharing our code benefits us all . . . . . . . . . . . . . . . . . . . . . . 197 Writing packages, not programs . . . . . . . . . . . . . . . . . . . . . 198 Command‐line tools . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 Zen mountaineering . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 Guided by tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 Building a hello package . . . . . . . . . . . . . . . . . . . . . . . . . . . 200 The structure of a test . . . . . . . . . . . . . . . . . . . . . . . . . . . 201 7
📄 Page
8
Tests are bug detectors . . . . . . . . . . . . . . . . . . . . . . . . . . 202 So when should a test fail? . . . . . . . . . . . . . . . . . . . . . . . . 202 Where does fmt.Println print to? . . . . . . . . . . . . . . . . . . . 203 Meet bytes.Buffer, an off‐the‐shelf io.Writer . . . . . . . . . . . . 204 Failure standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 Implementing hello, guided by tests . . . . . . . . . . . . . . . . . . . . . 205 We start with a failing test . . . . . . . . . . . . . . . . . . . . . . . . 205 Creating a module . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 One folder, one package . . . . . . . . . . . . . . . . . . . . . . . . . 206 A null implementation . . . . . . . . . . . . . . . . . . . . . . . . . . 207 The real implementation . . . . . . . . . . . . . . . . . . . . . . . . . 207 The naming of tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 Refactoring to use our new package . . . . . . . . . . . . . . . . . . . 208 Going further . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 8
📄 Page
9
Praise for For the Love of Go A great way to dive into Go! —Max VelDink One of the best technical books I have read in a very long time. —Paul Watts John is both a superb engineer and, just as importantly, an excellent teacher / communicator. —Luke Vidler A fantastic example of good technical writing. Clear, concise and easily digestible. —Michael Duffy This book’s writing style feels as if John is speaking to you in person and helping you along the way. —@rockey5520 Very well written, friendly, and informative. —Chris Doyle John’s writing is personable, human and funny—his examples are realistic and rel‐ evant. The test‐driven instruction teaches Go in a deep, meaningful and engaging way. —Kevin Cunningham The book takes a very easy‐to‐follow, step‐by‐step approach to Go. The writing is very friendly and accessible. This would probably be my pick for the absolute beginner to computer programming who wants to learn Go. —Jonathan Hall 9
📄 Page
10
Introduction Hello, and welcome to learning Go! It’s great to have you here. What’s this? This book is an introduction to the Go programming language, suitable for complete beginners. If you don’t know anything about Go yet, or programming, but would like to learn, you’re in the right place. If you do already have some experience with Go, or with programming in other lan‐ guages, don’t worry: this book is for you too! You’ll learn some ideas, techniques, and ways of tackling problems that even many advanced Go programmers don’t know. I hope to also help you fill in a few gaps in your knowledge that you may not even be aware of. What you’ll need You’ll need to install Go on your computer, if you don’t have it already. Follow the in‐ structions on the Go website to download and install Go: • https://go.dev/learn/ 10
📄 Page
11
While all you need to write and run Go programs is a terminal and a text editor, you’ll find it very helpful to use an editor that has specific support for Go. For example, Visual Studio Code has excellent Go integration. There’s a dedicated Go editor called GoLand that you may come across: I don’t recom‐ mend this, at least for beginners. Although GoLand is a very powerful tool for profes‐ sional developers, it makes some non‐obvious choices about how to actually represent your Go code on screen, and this can be confusing for newcomers. By all means try out GoLand to see if you like it, but I’d recommend starting with Visual Studio Code and get‐ ting used to that first. While not essential, it’s a great idea to use some kind of version control software (for ex‐ ample, Git) to track and share your source code. I won’t go into the details of how to install and use Git in this book, but if you’re not already familiar with it, I recommend you learn at least the basics. Go to GitHub to find out more. Where to find the code examples There are dozens of challenges for you to solve throughout the book, each designed to help you test your understanding of the concepts you’ve just learned. If you run into trouble, or just want to check your code, each challenge is accompanied by a complete sample solution, with tests. All these solutions are also available in a public GitHub repo here: • https://github.com/bitfield/ftl‐code Each listing in the book is accompanied by a number (for example, Listing 1.1, and you’ll find the solution to that exercise in the numbered folder of the repo. What you’ll learn By reading through this book and completing the exercises, you’ll learn: • How to write tests in Go and how to develop projects guided by tests • How to manage data in Go using built‐in types, user‐defined struct types, and col‐ lections such as maps and slices • How to write and test functions, including functions that return multiple results and error values • How to use objects to model problems in Go, and how to add behaviour to objects using methods • How to use pointers to write methods that modify objects, and how to use types and validation to make your Go packages easy to use 11
📄 Page
12
The love of Go Go is an unusually fun and enjoyable language to write programs in, and I hope to com‐ municate something of my own love of Go to you in this book. It’s a relatively simple language, which isn’t the same as easy, of course. Programming is not easy, and no lan‐ guage makes it easy. Some just make it a little more fun than others. Go lacks many things that other programming languages have, and that’s no accident. In some ways, the fact that Go doesn’t have many features is its best feature. It has enough features to do everything we need to do in building software, but no more. The fewer features there are, the less we have to learn, and the more quickly we can get on to using our new tool to do interesting things. Because each little bit of code doesn’t really do much by itself, it’s quite easy to read Go programs, even if you haven’t got much experience with Go. As programmers, we don’t need to worry about choosing among many possible ways to implement the same logic. This leaves us free to think about the much more important issue of how to structure our programs in clear, simple ways that are easy for readers to understand. Go is humble, and that’s a good attitude for us to adopt, too. It doesn’t claim to be the world’s greatest programming language, the most advanced, or the most pure, elegant, and theoretically appealing. It isn’t. The designers of Go wanted to maximise productiv‐ ity, not elegance, and that explains a lot. Similarly, the programs we write with Go should be simple, straightforward, and prag‐ matic. If they’re elegant too, that’s nice, but it’s not the goal. And because Go programs tend to be simple and easy to understand, it’s easy to change them later, so we don’t need to over‐engineer everything at the start. We just build what’s necessary to get the job done and leave it at that. We try to solve the problem we have, not the problem that might face us next week, or next year. Rather than blindly applying coding dogmas like “always write short func‐ tions”, regardless of the situation, Go programmers are more interested in what makes the program clearer. Programming with confidence It’s very important that the programs we write be correct. That is, they should do what they’re intended to do. Indeed, if the program is not correct, hardly anything else about it matters. One way to improve our confidence in the program’s correctness is to write tests for it. A test is also a program, but it’s a program specifically designed to run another program with various different inputs, and check its result. The test verifies that the program actually behaves the way it’s supposed to. For example, if you were considering writing a function called Multiply, and you 12
📄 Page
13
wanted to be able to check that it worked, you might write a program that calls Mul- tiply(2, 2) and checks that the answer is 4. That’s a test! And it turns out that it’s a good idea to write the test even before you start writing the function it tests. It might seem a little odd to do things this way round, if you’re not used to it. But it actually makes a lot of sense. By writing the test first, you are forced to think about what the program actually needs to do. You also have to design its interface (the way users interact with the program). If it’s really awkward to call your function, or it has a name that doesn’t make sense in the context where it’s used, all of these things will be obvious when you write your test. In effect, you’re being your own first user. And it also means you know when to stop coding, because when the test starts passing, you’re done! So writing tests is not just about gaining confidence that your code is correct, although that’s definitely useful. It’s also a powerful process for designing well‐structured programs with good APIs. A phrase I like that describes this process is “development, guided by tests”. What makes this book different? Most Go books will start by telling you about the basic syntax of the language, variables, data types, and so on. That’s important stuff to know, and we’ll cover it in this book too. But because I want you to get thoroughly comfortable with the process of software de‐ velopment guided by tests, we’re going to be doing that right from the start. In order to do this, we’ll use some Go concepts that won’t be familiar to you yet: func‐ tions, variables, the testing package, and so on. Don’t worry about understanding everything that’s going on at first: we’ll get to the details later. For the first few chapters, just relax and enjoy the ride, even if you come across some things that seem a little mys‐ terious. In fact, this is often the situation we find ourselves in as programmers: we have some existing code that we don’t necessarily totally understand, but we will try to figure out just enough to solve the problem at hand. So this will be good practice for you, and everything will be explained in its right place. How to use this book Throughout this book we’ll be working together to develop a project in Go. Each chapter introduces a new feature or concept, and sets you some goals to achieve. Goals are marked with GOAL in bold. (Don’t worry if you’re not sure how to start. There’ll usually be some helpful suggestions following the goal description.) So let’s get started! 13
📄 Page
14
1. Testing times It’s your first day as a Go developer at Texio Instronics, a major manufacturer of widgets and what‐nots, and you’ve been assigned to the Future Projects division to work on a very exciting new product: a Go‐powered electronic calculator. Welcome aboard. In this exercise, all we need to do is make sure our Go environment is set up and everything is working right. We’re going to create a new Go project for the calculator package, add some test code to it, and run the test. Creating a new project Every Go project needs two things: a folder on disk to keep its source code in, and a go.mod file which identifies the module, or project name. If you don’t have one already, I recommend you create a new folder on your computer to keep your Go projects in (this can be in any location you like). Then, within this folder, create a subfolder named calculator. Next, start a shell session using your terminal program (for example, the macOS Ter‐ minal app) or your code editor. Set your working directory to the project folder using the cd command (for example, cd ~/go/calculator). Now run the following command in the shell: 14
📄 Page
15
go mod init calculator go: creating new go.mod: module calculator You should find that a new file has been created named go.mod, containing: module calculator (Listing 1.1) Go organises code into units called modules, and each module needs to be in a separ‐ ate folder (you can’t have modules within modules). Each module needs a go.mod file which tells Go that this folder contains a module, and what the name of the module is (in this case, it’s calculator). So we should now have a folder structure something like this: calculator/ go.mod All clear? Great! We’ll be creating a number of new Go projects throughout this book, using the same steps shown in this section: create a new folder, and inside the folder, run the command go mod init MODULE_NAME, where MODULE_NAME represents the name we want to give the project. Next, we’ll see how to create some Go files and run tests. Creating Go files So that we don’t have to start entirely from scratch, a helpful colleague at Texio Instron‐ ics has sent us some Go code that implements part of the calculator’s functionality, plus a test for it. In this section we’ll add that code to the project folder. First, using your code editor, create a file in the calculator folder named calcu- lator.go. Copy and paste the following code into it: // Package calculator does simple calculations. package calculator // Add takes two numbers and returns the result of adding // them together. func Add(a, b float64) float64 { return a + b } (Listing 1.1) If it’s not easy for you to copy and paste the code from this book, clone the GitHub re‐ pository instead and copy from there: 15
📄 Page
16
• https://github.com/bitfield/ftl‐code Don’t worry about understanding all the code here for the moment; we’ll cover this in detail later. For now, just paste this code into the calculator.go file and save it. Next, create another file named calculator_test.go containing the following: package calculator_test import ( "calculator" "testing" ) func TestAdd(t *testing.T) { t.Parallel() var want float64 = 4 got := calculator.Add(2, 2) if want != got { t.Errorf("want %f, got %f", want, got) } } (Listing 1.1) Here’s the folder structure we should end up with: calculator/ calculator.go calculator_test.go go.mod Again, you don’t need to fully understand what this code is doing yet (but you might be able to guess some of it). Soon, you’ll not only understand how this works, but you’ll be writing your own tests. But before we get to that, we need to make sure that our Go environment is properly installed and working, the project is set up correctly, and that we can successfully run tests for a project. Running the tests Even though it’s pretty small, this is a fully‐functioning Go project, and as all good pro‐ jects should, it contains some tests. The next thing for us to do is to run them. 16
📄 Page
17
GOAL: Run the tests for this project and make sure they pass. The following instruc‐ tions will show you how to do that. Still in the calculator folder, run the command: go test If everything works as it should, you will see this output: PASS ok calculator 0.234s This tells us that all the tests passed in the package called calculator, and that run‐ ning them took 0.234 seconds. (It might take a longer or shorter time on different com‐ puters, but that’s okay.) If you see test failure messages, or any other error messages, there may be a problem with your Go setup. Don’t worry, everybody runs into this kind of issue at first. Try Googling the error message to see if you can figure out what the problem is, and check the installation instructions on the Go website here: • https://go.dev/learn/ Once the test is passing, you can move on! Try using your editor’s Go support features to run the test directly in the editor. For ex‐ ample, in Visual Studio Code, there’s a little Run test link above each test function, and a Run package tests link at the top of the file. Find out how to do this in the editor you’re using, and run the test successfully. Formatting code with gofmt The next thing to do is ensure that our Go code is formatted correctly. All Go code is formatted in a standard way, using a tool that comes with Go called gofmt (pro‐ nounced, in case you were wondering, “go‐fumpt”). In this exercise we’ll see how to use it to check and reformat Go code (if necessary). First, let’s introduce a deliberate formatting error into the code, so that we can try to find it with gofmt. Edit the calculator.go file and look for this line: func Add(a, b float64) float64 { (Listing 1.1) Insert an extra space before the word Add, so that the line looks like this: func Add(a, b float64) float64 { GOAL: Run gofmt to check the formatting and find out what is wrong: gofmt -d calculator.go 17
📄 Page
18
... // Add takes two numbers and returns the result of adding // them together. -func Add(a, b float64) float64 { +func Add(a, b float64) float64 { return a + b } Ignore the preamble for the moment, and focus on the lines beginning with - and +: -func Add(a, b float64) float64 { +func Add(a, b float64) float64 { gofmt is telling us what it wants to do. It wants to remove (-) the line that has the bogus extra space before the Add function, and replace it (+) with a line where the space has been removed. NOTE: If you’re using Windows, you may see an error like this: computing diff: exec: "diff": executable file not found in %PATH% This is because the gofmt tool relies on there being a diff program in your PATH that it can use to generate a visual diff of the file. If you don’t have diff installed, or if it’s not in your default PATH, this won’t work. Don’t worry, though: this isn’t a serious prob‐ lem, and it won’t stop you being able to develop with Go; it just means you won’t be able to use the gofmt -d command to check your formatting. You may find this Stack Overflow discussion helpful for resolving this issue: • gofmt -d error on Windows Fixing format with gofmt -w We could make the necessary formatting change using an editor, but there’s no need: gofmt can do it for us automatically. GOAL: Run: gofmt -w calculator.go This will reformat the file in place. Check that it’s worked by running gofmt -d on the file again. Now there should be no output, because it’s already formatted correctly. If you’re using an editor that has Go support, set up the editor to automatically run your code through gofmt every time you save it. This will help you avoid forgetting to format your code. Just for fun, try experimenting with gofmt a little. Try formatting code in different ways and see what changes gofmtmakes to it. It’s interesting to note what it does and doesn’t change. 18
📄 Page
19
If you like gofmt, there’s a similar tool named gofumpt that works in much the same way, but does slightly more reformatting than the standard gofmt tool. Functions in Go Let’s take a closer look now at the Add function and identify some important parts of function syntax in Go: func Add(a, b float64) float64 { return a + b } (Listing 1.1) This is a function declaration. It tells Go the name of the function we’re going to write, any parameters that it takes as its input, and any results that it returns. Then come some statements within curly braces, known as the body of the function. That’s the bit that actually does something. A function declaration is introduced by the keyword func (meaning “here comes a function!”). Following func is the name of the function (Add), and a list of its paramet‐ ers in parentheses (a, b float64). Then comes the list of results it returns (if any). Here that’s a single float64 value, and it doesn’t have a name, so we write just float64 before the opening curly brace ({). Inside the curly braces is a list of statements that form the body of the function. In our example, there’s only one statement: return a + b. This ends the function, returning the value of a + b as its float64 result. We’ll look at functions in much more detail later on in the book, in the chapter on “Fun with functions”. For now, though, let’s continue working on the calculator project. A failing test Now that our development environment is all set up, a colleague needs our help. She has been working on getting the calculator to subtract numbers, but there’s a problem: the test is not passing. Let’s see what we can do to fix it. Copy and paste the following code into the calculator_test.go file, after the Test- Add function: func TestSubtract(t *testing.T) { t.Parallel() var want float64 = 2 got := calculator.Subtract(4, 2) if want != got { 19
📄 Page
20
t.Errorf("want %f, got %f", want, got) } } (Listing 3.1) Here’s the code she’s using to implement the Subtract function. Copy this into the calculator.go file, after the Add function: // Subtract takes two numbers a and b, and // returns the result of subtracting b from a. func Subtract(a, b float64) float64 { return b - a } (Listing 3.1) Now, apparently this code is not working, so our challenge is to figure out why not, and fix it. Fortunately, we have a failing test to help us. Save the modified files, and run the tests with the following command: go test --- FAIL: TestSubtract (0.00s) calculator_test.go:22: want 2.000000, got -2.000000 FAIL exit status 1 FAIL calculator 0.178s There’s a lot of helpful information here, so let’s learn how to interpret this test output. First of all, something is failing, because we see the word FAIL. The next thing we want to know is what’s failing, and why. We have two tests, and only one of them is failing at the moment. Which one? The out‐ put from go test tells us the name of the failing test: --- FAIL: TestSubtract (0.00s) And that’s a good start, but we can learn still more. The output tells us the exact source file and line number where the test failure happened: calculator_test.go:22 Let’s have another quick look at the test to find line 22: func TestSubtract(t *testing.T) { t.Parallel() var want float64 = 2 got := calculator.Subtract(4, 2) if want != got { 20
The above is a preview of the first 20 pages. Register to read the complete e-book.