Statistics
62
Views
0
Downloads
0
Donations
Support
Share
Uploader

高宏飞

Shared on 2025-11-25

AuthorJohn Arundel

This book assumes no previous knowledge of programming, and gently introduces you to Go step by step, challenging you to apply each new concept as you learn it. If you’ve used Go before but feel somehow you skipped something important, this book will build your confidence in the fundamentals. Take your first steps toward mastery with my fun, readable, and easy-to-follow guide. Throughout the book we'll be working together to develop a fun and useful project in Go: an online bookstore called Happy Fun Books. You’ll learn how to use Go to store data about real-world objects such as books, how to write code to manage and modify that data, and how to build useful and effective programs around it. Each chapter introduces a new feature or concept, and sets you some goals to achieve, with complete, step-by-step explanations of how to solve them, and full code listings with accompanying tests. What you’ll learn By reading this book and working through all the challenges and 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 collections such as maps and slices - 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 for objects, and how to use types and validation to make your Go packages a delight to use - How to build powerful, flexible programs using control structures like loops and functions Even more importantly, you’ll learn a simple, fun, and effective way to approach software engineering projects in Go. Even though the material is beginner-friendly, you’ll be mastering some advanced techniques, and learning to design modules, packages, and APIs. These are essential skills in any modern software engineering role.

Tags
No tags
Publisher: John Arundel
Publish Year: 2025
Language: 英文
Pages: 217
File Format: PDF
File Size: 1.9 MB
Support Statistics
¥.00 · 0times
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.

(This page has no text content)
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 . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 if statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24 Conditional expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25 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
Receiving the error value . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 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 . . . . . . . . . . . . . . . . . . . . . . . . 44 A Sqrt function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45 Running programs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 The main package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46 The go run command . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47 The go build command . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49 4. Happy Fun Books 51 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51 Variables and values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 Type checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54 More types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55 Zero values and default values . . . . . . . . . . . . . . . . . . . . . . . . . 55 Introducing structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56 Type definitions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 Exported identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 The core package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57 The very first test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Testing the core struct type . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Struct literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58 Assigning a struct literal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 The unfailable test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 60 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61 5. Story time 62 User stories . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 What are the core stories? . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 The first story . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63 Struct variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 The short declaration form . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 Referencing struct fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Writing the test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65 Getting to a failing test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66 Assigning to struct fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 Implementing Buy . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67 3
Test coverage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68 Test‐last development . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69 Uncovering a problem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 “Covered” versus “tested” . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Juking the stats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73 6. Slicing & dicing 75 Slices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Slice variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75 Slice literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 Slice indexes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 Slice length . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76 Modifying slice elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Appending to slices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 A collection of books . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77 Setting up the world . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 Comparing slices (and other things) . . . . . . . . . . . . . . . . . . . . . . 78 Unique identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 Getting to a failing test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82 Finding a book by ID . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83 Crime doesn’t pay . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86 7. Mapmischief 87 Introducing the map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 Adding a new element to a map . . . . . . . . . . . . . . . . . . . . . . . . 89 Accessing element fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89 Updating elements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 Non‐existent keys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90 Checking if a value is in the map . . . . . . . . . . . . . . . . . . . . . . . . 91 Returning an error . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92 Testing the invalid input case . . . . . . . . . . . . . . . . . . . . . . . . . . 93 Updating GetAllBooks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 Sorting slices . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Function literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 8. Objects behaving badly 100 Objects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100 Doing computation with objects . . . . . . . . . . . . . . . . . . . . . . . . 101 Testing NetPriceCents . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102 Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 Defining methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103 Methods on non‐local types . . . . . . . . . . . . . . . . . . . . . . . . . . 104 Creating custom types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105 4
More types and methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 Creating a Catalog type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 9. Wrapper’s delight 112 The strings.Builder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 Creating a type based on strings.Builder . . . . . . . . . . . . . . . . . . 114 Wrapping strings.Builder with a struct . . . . . . . . . . . . . . . . . . . 115 A puzzle about function parameters . . . . . . . . . . . . . . . . . . . . . . 118 Parameters are passed by value . . . . . . . . . . . . . . . . . . . . . . . . 119 Creating a pointer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119 Declaring pointer parameters . . . . . . . . . . . . . . . . . . . . . . . . . 120 What can we do with pointers? . . . . . . . . . . . . . . . . . . . . . . . . . 120 The * operator . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120 Nil desperandum . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 Pointer methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 121 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 10. Very valid values 124 Validating methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124 Automatic dereferencing . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126 Always valid fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 Unexported fields and cmp.Equal . . . . . . . . . . . . . . . . . . . . . . . 131 Always valid structs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132 A set of valid values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135 Using a map to represent a set . . . . . . . . . . . . . . . . . . . . . . . . . 135 Defining constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136 Referring to constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 Standard library constants . . . . . . . . . . . . . . . . . . . . . . . . . . . 137 When the values don’t matter . . . . . . . . . . . . . . . . . . . . . . . . . 137 Introducing iota . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141 11: Opening statements 143 Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 Declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 What is assignment? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144 Short variable declarations . . . . . . . . . . . . . . . . . . . . . . . . . . . 145 Assigning more than one value . . . . . . . . . . . . . . . . . . . . . . . . . 145 The blank identifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146 Increment and decrement statements . . . . . . . . . . . . . . . . . . . . . 147 if statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 The happy path . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 else branches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 Early return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 Conditional expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 5
And, or, and not . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 bool variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 Compound if statements . . . . . . . . . . . . . . . . . . . . . . . . . . . 153 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154 12: Switch which? 156 The switch statement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 Switch cases . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 The default case . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157 Switch expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158 Exiting a switch case early with break . . . . . . . . . . . . . . . . . . . . . 158 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 The for keyword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159 Forever loops . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160 Using range to loop over collections . . . . . . . . . . . . . . . . . . . . . . 160 Receiving index and element values from range . . . . . . . . . . . . . . . . 161 Conditional for statements . . . . . . . . . . . . . . . . . . . . . . . . . . 162 Init and post statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162 range over integer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 Jumping to the next element with continue . . . . . . . . . . . . . . . . . . 163 Exiting loops with break . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164 Controlling nested loops with labels . . . . . . . . . . . . . . . . . . . . . . 165 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166 13: Fun with functions 168 Declaring functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 Parameter lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 Result lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169 The function body . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 Calling a function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 170 Using result values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 Return statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 Function values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 Function literals . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 Closures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175 Cleaning up resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 The defer keyword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 Multiple defers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 Named result parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 Naked returns considered harmful . . . . . . . . . . . . . . . . . . . . . . . 179 Modifying result parameters after exit . . . . . . . . . . . . . . . . . . . . . 179 Deferring a function literal . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 Deferring a closure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180 Variadic functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 6
14: Building blocks 184 Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 184 The main package . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 The main function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 The init function . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185 Alternatives to init . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 Building an executable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186 The executable binary file . . . . . . . . . . . . . . . . . . . . . . . . . . . 187 Cross‐compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 Exiting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188 Takeaways . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 15. The Tao of Go 191 Kindness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191 Simplicity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192 Humility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193 Not striving . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 The love of Go . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 About this book 196 Who wrote this? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196 Free updates to future editions . . . . . . . . . . . . . . . . . . . . . . . . . 197 Join my Go Club . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 Video course . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 197 The Power of Go: Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 The Power of Go: Tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 Know Go: Generics . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 Further reading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 Credits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199 Acknowledgements 200 A sneak preview 201 1. Packages 202 The universal library . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 202 Packages are a force multiplier . . . . . . . . . . . . . . . . . . . . . . 203 The universal Go library is huge . . . . . . . . . . . . . . . . . . . . . 203 Sharing our code benefits us all . . . . . . . . . . . . . . . . . . . . . . 204 Writing packages, not programs . . . . . . . . . . . . . . . . . . . . . 205 Command‐line tools . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 Zen mountaineering . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 Guided by tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 207 Building a hello package . . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 The structure of a test . . . . . . . . . . . . . . . . . . . . . . . . . . . 208 7
Tests are bug detectors . . . . . . . . . . . . . . . . . . . . . . . . . . 209 So when should a test fail? . . . . . . . . . . . . . . . . . . . . . . . . 210 Where does fmt.Println print to? . . . . . . . . . . . . . . . . . . . . 210 Meet bytes.Buffer, an off‐the‐shelf io.Writer . . . . . . . . . . . . . 211 Failure standards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212 Implementing hello, guided by tests . . . . . . . . . . . . . . . . . . . . . 212 We start with a failing test . . . . . . . . . . . . . . . . . . . . . . . . 213 Creating a module . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213 One folder, one package . . . . . . . . . . . . . . . . . . . . . . . . . 214 A null implementation . . . . . . . . . . . . . . . . . . . . . . . . . . 214 The real implementation . . . . . . . . . . . . . . . . . . . . . . . . . 215 The naming of tests . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215 Refactoring to use our new package . . . . . . . . . . . . . . . . . . . 216 Going further . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 217 8
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
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
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
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
wanted to be able to check that it worked, you might write a program that calls Multiply(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 ev‐ erything 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 ev‐ erything 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 chap‐ ter 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
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 every‐ thing is working right. We’re going to create a new Go project for the calculator pack‐ age, 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 Termi‐ nal 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
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 sepa‐ rate 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 calcula- tor.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 repository instead and copy from there: 15
• 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 projects should, it contains some tests. The next thing for us to do is to run them. 16
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 running them took 0.234 seconds. (It might take a longer or shorter time on different computers, 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 for‐ matted in a standard way, using a tool that comes with Go called gofmt (pronounced, 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: 17
gofmt -d calculator.go ... // 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 problem, 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. 18
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. 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 func‐ tion!”). Following func is the name of the function (Add), and a list of its parameters 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 TestAdd function: 19
func TestSubtract(t *testing.T) { t.Parallel() var want float64 = 2 got := calculator.Subtract(4, 2) if want != got { 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 cal- culator.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: 20