Go Module 2 – Basics: values, types and control flow
Variables, short declarations and zero values
Declare Go variables with var and :=, predict the zero value of every type, swap two values in one assignment and find the := that hides an error.
What you will learn
- Declare variables with var and := and choose the right form for each place
- Predict the zero value of numbers, strings, pointers, slices, maps and structs
- Swap two values in one assignment and discard a value with the blank identifier
- Detect a variable shadowed by := in an inner block and fix it
Before you start
What you’ll learn · Before you start
What you will learn
- Declare variables with var and := and choose the right form for each place
- Predict the zero value of numbers, strings, pointers, slices, maps and structs
- Swap two values in one assignment and discard a value with the blank identifier
- Detect a variable shadowed by := in an inner block and fix it
Before you start
On this page
A Go function asked to read the limit “25O”, with a letter O where a zero belongs, returned 100 and no error. The failure did happen: it was stored in a variable and lost on the way out. Nothing crashed, because every Go variable holds a value from the moment it exists, and the variable the caller saw still held its default.
That bug comes from the ideas of this lesson: how var and := declare variables, the zero value each type starts
with, and the blocks that decide which variable a name means. You will choose the right declaration for each place,
predict zero values, swap two values in one assignment, and find and fix a shadowed variable.
var gives a variable a type, a value or both
A declaration names a variable and gives it a type and a first value. With var you can write the type, the value or
both, and Go works out the rest. This program declares eight variables for a train booking, five inside main and
three at package level, outside every function, and prints each one’s type with %T and its value with %v:
// declare.go: the three forms of var, a var block, and package-level variables.
package main
import "fmt"
// Package-level variables may use each other in any order: Go sets fare and
// seats first, because total depends on them.
var total = fare * seats
var fare = 1999 // rupees
var seats = 3
func main() {
var berth int // a type and no value: berth starts at 0
var coach = "B2" // a value and no type: coach gets the value's type
var rate float64 = 18 // both: the constant 18 becomes a float64
var (
booked bool
waiting = 4
)
fmt.Printf("berth %-7T %v\n", berth, berth)
fmt.Printf("coach %-7T %v\n", coach, coach)
fmt.Printf("rate %-7T %v\n", rate, rate)
fmt.Printf("booked %-7T %v\n", booked, booked)
fmt.Printf("waiting %-7T %v\n", waiting, waiting)
fmt.Printf("total %-7T %v\n", total, total)
} Output
berth int 0 coach string B2 rate float64 18 booked bool false waiting int 4 total int 5997
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run declare.go
The first three lines of output show the three forms:
| You write | The variable’s type | Its first value |
|---|---|---|
var berth int |
the type you name, int |
the zero value of int, 0 |
var coach = "B2" |
the type of the value, string |
"B2" |
var rate float64 = 18 |
the type you name; the constant 18 converts to it | 18 |
Without a type, a variable takes the type of its value. A whole number such as 4 gives int, a number with a
decimal point such as 4.5 gives float64, and text in quotes gives string. At least one of the two must be there,
and var n = nil does not compile, because nil has no type to give. Parentheses group several declarations under
one var, as booked and waiting show.
total, fare and seats are package-level variable: A variable declared outside every function. It exists for the whole run, and Go gives it its value before main starts, setting each one after the variables its value depends on.. Go gives them their
values before main starts, and it orders that work by what each value needs, not by the order of the lines: total
comes first in the file, but its value uses fare and seats, so those two are set first and total comes out as
5997. Inside a function the order of the lines is the order of the work, and a variable can be used only after its
declaration.
Checkpoint
Check: var with a type, a value or both
A short declaration declares and assigns in one step
Inside functions there is a shorter form, :=, the
short variable declaration: The form name := value, which declares new variables with the types of their values. It may appear only inside functions, and it must declare at least one new variable on its left.. w, h := 1920, 1080 means the same as
var w, h = 1920, 1080: two new variables, each with the type of its value. It is shorter than var, and it has two
rules of its own. It works only inside functions, so a package-level variable always needs var. And it must declare
at least one new variable on its left:
// short.go: := declares new variables, and may assign one that exists.
package main
import (
"fmt"
"strconv"
)
func main() {
w, h := 1920, 1080 // two new variables, typed by their values
fmt.Printf("w %T %v, h %T %v\n", w, w, h, h)
n, err := strconv.Atoi("42") // n and err are new
m, err := strconv.Atoi("4x2") // m is new; err is assigned again
fmt.Println(n, m, err)
} Output
w int 1920, h int 1080 42 0 strconv.Atoi: parsing "4x2": invalid syntax
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run short.go
In m, err := strconv.Atoi("4x2"), m is new and err already exists in the same block, so err is not declared a
second time: it is the same variable, given a new value, the error that Atoi returned for the text “4x2”. The
specification calls this redeclaration, and it is what lets one err serve a whole series of calls. With nothing new on the left, the
line does not compile:
// no_new.go: a second := that declares nothing new.
package main
import "fmt"
func main() {
count := 1
count := 2
fmt.Println(count)
} Output (exit status 1)
Printed as an error (standard error)
# command-line-arguments ./no_new.go:8:8: no new variables on left side of :=
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run no_new.go
The compiler stopped at line 8 with no new variables on left side of :=. To give a variable that exists a new value,
write a plain assignment, count = 2.
So which form should a variable get? It depends on where it stands and what it starts with. Answer the three questions about one variable of your own, and the table rules out the forms that do not fit:
Answer the questions above: the table then marks what fits your case and says why the rest does not.
| Declaration | At package level | Starts at zero | A type the value lacks | In an if or for header |
|---|---|---|---|---|
| var n int Its type comes from: the type you name; the value is zero | Good allowed in and out of functions | Good says plainly that 0, "" or nil is the start | Good names any type you need | Poor an if, for or switch header takes := only |
| var n = 5 Its type comes from: the value | Good allowed in and out of functions | Mixed works, but spells the zero value out | Poor takes its value's default type, such as int, float64 or string | Poor an if, for or switch header takes := only |
| var n int64 = 5 Its type comes from: the type you name; the value converts | Good allowed in and out of functions | Mixed works, but spells the zero value out | Good the constant 5 becomes an int64 | Poor an if, for or switch header takes := only |
| n := 5 Its type comes from: the value | Poor not allowed outside a function | Mixed needs the value written: n := 0 | Mixed needs a conversion: n := int64(5) | Good if n, err := f(); err != nil { … } works |
How the answers decide
- “Where is the variable declared?” At package level, outside every function: rules out n := 5. Short declarations are allowed only inside functions; at package level every declaration starts with a keyword such as var.
- “Where is the variable declared?” In the header of an if, for or switch: rules out var n int, var n = 5 and var n int64 = 5. The header of an if, for or switch can declare variables only with :=, and they exist until the end of that statement.
- “What should it hold first?” Its zero value, set later: adds a caveat to var n = 5, var n int64 = 5 and n := 5. These forms must write the zero value out; var n int starts at zero without one.
- “What should it hold first?” A value you have now: rules out var n int. var n int gives no value, so the variable starts at zero and needs a second line to take yours.
- “Must it have a type its value would not give it, such as int64 or float32?” Yes: rules out var n = 5 and adds a caveat to n := 5. Without a named type a variable takes the default type of its value, so := needs a conversion such as int64(5) to get another.
In short: := for most variables inside a function, var name Type when the variable should start at its zero value,
var with a type and a value when the value’s own type is the wrong one, and var for everything at package level.
Checkpoint
Check: Short declarations and their new-variable rule
One assignment can set several variables at once
w, h = h, w exchanges two variables in one line, with no temporary variable. It works because an assignment runs in
two phases: first every expression on the right is evaluated, then the variables on the left are assigned, from left
to right. Step through the swap, and then the same exchange written as two statements:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- Two variables. w holds 1920 and h holds 1080, the width and the height of a screen. One statement should exchange them, w, h = h, w.
- Phase 1: read. Go first evaluates every expression on the right, in order, before it changes anything. It reads h, which is 1080, then w, which is still 1920, and keeps both values.
- Phase 2: assign w. Only then are the assignments carried out, from left to right. w takes the first value read, 1080.
- Phase 2: assign h. h takes the second value, 1920, which was read before w changed. The two have swapped with no temporary variable, and the program prints one assignment, 1080 1920.
- Two lines instead. The same exchange as two statements uses x and y, again holding 1920 and 1080. Each statement runs to its end before the next one starts.
- x = y. x = y copies 1080 into x. The 1920 that x held is gone, and no variable holds it any more.
- y = x. y = x copies x into y, and x already holds 1080, so y keeps 1080. The program prints two assignments, 1080 1080.
The two-statement version lost a value: after x = y, the 1920 that x held is gone, so y = x copies 1080 back.
The program prints both results:
// swap.go: one assignment that swaps, and two that lose a value.
package main
import (
"fmt"
"strconv"
)
func main() {
w, h := 1920, 1080
w, h = h, w // both values on the right are read before either is assigned
fmt.Println("one assignment:", w, h)
x, y := 1920, 1080
x = y
y = x // x already holds 1080, so y gets 1080 as well
fmt.Println("two assignments:", x, y)
_, err := strconv.Atoi("7") // _ throws the number away
fmt.Println("error:", err)
} Output
one assignment: 1080 1920 two assignments: 1080 1080 error: <nil>
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run swap.go
When a call returns two values and you need only one, the blank identifier: The name _ (an underscore), which takes a value and throws it away. It lets code ignore a result on purpose, or import a package only for what it does when it starts. More in “Your first Go program: go run and go build” _
takes the place of the other: _, err := strconv.Atoi("7") keeps the error and throws the number away. _ is never a
new variable. In a block that already has an err, _, err := … fails like no_new.go, because nothing on its left is
new; write _, err = … there.
Checkpoint
Check: Assigning several variables at once
Every variable starts at its type’s zero value
Go never hands you an uninitialised variable. A declaration without a value, a struct field you did not set, the elements of a new array: each starts at the zero value: The value a variable holds when it is given none: 0 for numbers, false for booleans, the empty string for strings, nil for pointers, slices, maps, channels, functions and interfaces, and zeroed fields or elements for structs and arrays. of its type. That is 0 for numbers, false for booleans, the empty string for strings, and nil: The zero value of pointers, slices, maps, channels, functions and interfaces: nothing is there yet. A nil slice or map has length 0 and can be read, but writing to a nil map panics. for pointers, slices, maps, channels, functions and interfaces. Structs and arrays are zeroed part by part. This program declares twelve variables, one of each kind, and gives none of them a value:
// zero_values.go: what each kind of variable holds before you give it a value.
package main
import "fmt"
type Ticket struct {
PNR string
Seat int
Paid bool
Fares []int
}
func main() {
var (
i int
f float64
b bool
s string
p *int
sl []int
m map[string]int
ch chan int
fn func()
e error
arr [3]int
t Ticket
)
fmt.Println("int, float64, bool:", i, f, b)
fmt.Printf("string: %q, length %d\n", s, len(s))
fmt.Println("pointer, slice, map are nil:", p == nil, sl == nil, m == nil)
fmt.Println("channel, func, error are nil:", ch == nil, fn == nil, e == nil)
fmt.Println("array:", arr)
fmt.Printf("struct: %+v\n", t)
fmt.Println("len of the nil slice and map:", len(sl), len(m))
fmt.Println("reading a missing key of the nil map:", m["B2"])
sl = append(sl, 7)
fmt.Println("append to the nil slice:", sl)
} Output
int, float64, bool: 0 0 false
string: "", length 0
pointer, slice, map are nil: true true true
channel, func, error are nil: true true true
array: [0 0 0]
struct: {PNR: Seat:0 Paid:false Fares:[]}
len of the nil slice and map: 0 0
reading a missing key of the nil map: 0
append to the nil slice: [7]
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run zero_values.go
| Kind of type | Zero value | In the output |
|---|---|---|
Numbers (int, float64, byte …) |
0 | 0 0 |
bool |
false | false |
string |
the empty string, length 0 | "" |
Pointers, slices, maps, channels, functions, interfaces (error too) |
nil |
true for each == nil |
| Arrays | every element at its zero value | [0 0 0] |
| Structs | every field at its zero value | {PNR: Seat:0 Paid:false Fares:[]} |
%+v prints a struct with its field names: PNR: is followed by nothing because it holds the empty string, and
Fares:[] is a nil slice, printed as empty brackets. The last three lines show that nil is not broken: the nil slice
and the nil map both have length 0, reading a key that the nil map lacks gives the zero value of its values, 0, and
append turns the nil slice into [7].
Checkpoint
Check: The zero value of each kind of type
A useful zero value, and a map that refuses writes
Because every variable starts at zero, Go code tries to make the zero value useful. Effective Go points to the standard
library: the zero value of bytes.Buffer is an empty buffer ready to use, and the zero value of sync.Mutex is a
mutex that nobody holds, ready to lock, so neither needs a constructor. strings.Builder works the same way. A nil map is the case beginners
meet first: you can read it, but the first write stops the program.
// nil_map.go: a useful zero value, and a zero value that cannot take writes.
package main
import (
"fmt"
"strings"
)
func main() {
var b strings.Builder // the zero value is an empty builder, ready to use
b.WriteString("PNR ")
b.WriteString("4521")
fmt.Println(b.String())
var stock map[string]int // the zero value is a nil map: it can be read
fmt.Println("pens:", stock["pens"], "keys:", len(stock))
stock["pens"] = 12 // but not written
fmt.Println("never printed")
} Output (exit status 1)
PNR 4521 pens: 0 keys: 0
Printed as an error (standard error)
panic: assignment to entry in nil map goroutine 1 [running]: main.main() nil_map.go:17 +0x1cc exit status 2
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run nil_map.go
The builder needed no setting up, and reading the map worked, but stock["pens"] = 12 stopped the program at line 17
with panic: assignment to entry in nil map. A panic: A run-time error that stops the normal run of a program, such as writing to a nil map or dividing an integer by zero. Unless the program recovers, Go prints the error and the calls that led to it, and the program exits. ends the run, and go run reported the
program’s exit status, 2. The specification says that a nil map is like an empty map except that no elements may be
added. Give a map storage before its first write, with stock := map[string]int{} or make(map[string]int).
Declaring a map, then writing to it
var stock map[string]int followed by stock["pens"] = 12 panics, because the declaration made a nil map. Write
stock := map[string]int{} instead, which makes an empty map that takes writes.
A nil slice has no such problem, because append allocates the storage it needs, as zero_values.go showed with [7].
So of the two collections, only the map needs storage before its first write.
Key idea
Every variable starts at its type’s zero value. Design types whose zero value is a usable start, and give a map storage before you write to it: a nil map can be read but not written.
Checkpoint
Check: Useful zero values and nil maps
A short declaration inside a block hides the outer variable
A block is the code between a pair of braces, such as the body of an if. A variable declared in a block exists only
until its closing brace, and while it exists its name hides any variable of the same name outside, which is called
shadowing: Declaring a variable in an inner block with a name that a variable outside already has. Until the inner block's closing brace, the name means the inner variable, and the outer one is hidden.. A short declaration declares new variables whenever the block itself has none
of those names yet. So inside an if, limit, err := … makes two new variables, even when the function already has
a limit and an err. This is the function from the start of the lesson:
// shadow_bug.go: an inner := makes new variables, and the result is lost.
package main
import (
"fmt"
"strconv"
)
func parseLimit(s string) (int, error) {
limit := 100 // the default when s is empty
var err error
if s != "" {
limit, err := strconv.Atoi(s) // a new limit and a new err
fmt.Println(" inside the if:", limit, err)
}
return limit, err
}
func main() {
fmt.Println(parseLimit("250"))
fmt.Println(parseLimit("25O")) // a letter O, not a zero
} Output
inside the if: 250 <nil> 100 <nil> inside the if: 0 strconv.Atoi: parsing "25O": invalid syntax 100 <nil>
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run shadow_bug.go
Inside the if, the parse worked for “250” and failed for “25O”, yet both calls returned 100 <nil>: the values the
function’s own variables started with, which the block never touched. Step through the second call:
The drawing shows the whole scene; its 7 steps are listed below.
Every step as text
- The function's pair. parseLimit("25O") starts with two variables in the function's block. limit holds the default, 100, and err, declared with var, holds nil.
- A new block. s is not empty, so the if block runs. Its line limit, err := strconv.Atoi(s) finds neither name declared in this block yet, so := declares two new variables there.
- Atoi fails. Atoi cannot read "25O", which has a letter O where a zero belongs. It returns 0 and an error, and both go into the if block's pair.
- The print uses them. The print inside the block reads the inner pair and shows 0 and the error. Every variable is used, so the compiler has nothing to report.
- The block ends. At the closing brace the inner limit and err stop existing, and the error goes with them. The function's own pair was never touched.
- The return. return limit, err reads the function's variables, 100 and nil. The caller gets a limit that looks valid and no error.
- The fix: =. With = instead of :=, the same line assigns the function's limit and err. Atoi's 0 and its error then reach the return, and the caller sees the failure.
The fix is one character, = instead of :=, so that the line assigns the variables the function already has:
// shadow_fixed.go: = assigns the variables the function already has.
package main
import (
"fmt"
"strconv"
)
func parseLimit(s string) (int, error) {
limit := 100 // the default when s is empty
var err error
if s != "" {
limit, err = strconv.Atoi(s) // = writes the outer limit and err
}
return limit, err
}
func main() {
fmt.Println(parseLimit("250"))
fmt.Println(parseLimit("25O"))
} Output
250 <nil> 0 strconv.Atoi: parsing "25O": invalid syntax
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run shadow_fixed.go
Now “250” comes back as 250 and “25O” as 0 with its error. The compiler accepted the bug because every variable was
used: the print inside the block reads the inner pair. The standard checks of go vet do not look for shadowing either,
so the habit that protects you is a question at every := inside a block: do you mean a new variable?
Key idea
Inside a block, := makes new variables that hide the outer ones until the closing brace; to update a variable that
already exists, use =.
Checkpoint
Check: Shadowing by := in an inner block
Practise: parse a screen size
Write ParseDimensions, which reads a size such as “1920x1080” and returns the width, the height and an error. Put the
two files below in a folder of their own with a go.mod file whose first line is module example.com/dims, and run
go test there. The starter returns zeros and no error, so all four sample tests fail, as the run shows:
// Package dims reads sizes written as width x height.
package dims
// ParseDimensions reads a size such as "1920x1080" and returns the width
// and the height. It returns an error when s is not two whole numbers
// joined by an x.
func ParseDimensions(s string) (w, h int, err error) {
// Your code: cut s at the "x", convert each part with strconv.Atoi,
// and return the first error you meet.
return 0, 0, nil
} package dims
import "testing"
func TestParseDimensions(t *testing.T) {
cases := []struct {
in string
w, h int
wantErr bool
}{
{"1920x1080", 1920, 1080, false},
{"640x480", 640, 480, false},
{"1920y1080", 0, 0, true},
{"x1080", 0, 0, true},
}
for _, c := range cases {
w, h, err := ParseDimensions(c.in)
if (err != nil) != c.wantErr {
t.Errorf("ParseDimensions(%q): err = %v, want an error: %v", c.in, err, c.wantErr)
continue
}
if !c.wantErr && (w != c.w || h != c.h) {
t.Errorf("ParseDimensions(%q) = %d, %d, want %d, %d", c.in, w, h, c.w, c.h)
}
}
} from session import run
# The sample tests of the exercise, run against the starter code.
run(
"""
cd dims && go test
""",
expect_failure=["cd dims && go test"],
) Output
$ cd dims && go test
--- FAIL: TestParseDimensions (0.00s)
dims_test.go:23: ParseDimensions("1920x1080") = 0, 0, want 1920, 1080
dims_test.go:23: ParseDimensions("640x480") = 0, 0, want 640, 480
dims_test.go:19: ParseDimensions("1920y1080"): err = <nil>, want an error: true
dims_test.go:19: ParseDimensions("x1080"): err = <nil>, want an error: true
FAIL
exit status 1
FAIL example.com/dims 1.741s
[exit status 1]
This output changes from run to run: go test prints how long the run took, which differs from one run to the next.
Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 test_session.py
Cut the text at the x with strings.Cut, convert each part with strconv.Atoi, and return the first error you meet.
Use the named results w, h and err with =, not :=, inside any if. When the four tests pass, try inputs
the tests leave out, such as “ 1920x1080”, “-5x3” and “1920x”, and decide what each one should return.
Last comes a self-check on the six ideas above. Questions on the idea you answered worst so far come first, and each choice explains itself the moment you pick it:
Practice that adapts to you
Practise this lesson
14 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher): What is the zero value of a map, and what can you do with it?
The zero value of every map type is nil. A nil map can be read: indexing it gives the zero value of its value type, its
length is 0, a range loop over it runs no times, and deleting from it does nothing. It cannot be written: the first
assignment to a key panics with “assignment to entry in nil map”. So a map that will be written needs storage first, a
literal such as map[string]int{} or a call to make. A map field of a struct starts nil too, so give it storage in a
constructor, or allocate it in the method that writes to it, before the first write.
Two longer questions of the module bank build on this lesson:
Model answers open with any pass: ₹79 for 1 week ($2.99), one payment that never renews.
-
Fresher to mid
A function that loads a configuration file declares var err error at the top, calls os.ReadFile inside an if block and returns err at the end. Callers get a nil error even when the file is missing. What is the likely bug, how do you confirm it, and how do you keep it out of the codebase?
-
Fresher to mid
You are designing a WordTally type that counts words. Callers should be able to write var t WordTally and call t.Add("go") at once, the way they use strings.Builder. How do you make its zero value useful, and what goes wrong if it holds a map?
Key takeaways
varnames a type, a value or both; without a type, a variable takes its value’s type, and package-level variables get their values beforemain, in the order their values need.:=declares and assigns in one step, only inside functions, and it must declare at least one new variable on its left; the variables that already exist in the block are assigned.- An assignment evaluates everything on its right before it assigns, so
a, b = b, aswaps two variables, and_throws away a value you do not need. - Every variable starts at its type’s zero value: 0, false, the empty string, nil, or zeroed fields and elements.
- A nil slice can be appended to and a nil map can be read, but the first write to a nil map panics, so give a map storage before you write to it.
- Inside a block,
:=makes new variables that hide the outer ones until the closing brace; use=to update the variables you already have.
Terms in this lesson
- nil
- The zero value of pointers, slices, maps, channels, functions and interfaces: nothing is there yet. A nil slice or map has length 0 and can be read, but writing to a nil map panics.
- package-level variable
- A variable declared outside every function. It exists for the whole run, and Go gives it its value before main starts, setting each one after the variables its value depends on.
- panic
- A run-time error that stops the normal run of a program, such as writing to a nil map or dividing an integer by zero. Unless the program recovers, Go prints the error and the calls that led to it, and the program exits.
- shadowing
- Declaring a variable in an inner block with a name that a variable outside already has. Until the inner block's closing brace, the name means the inner variable, and the outer one is hidden.
- short variable declaration
- The form name := value, which declares new variables with the types of their values. It may appear only inside functions, and it must declare at least one new variable on its left.
- zero value
- The value a variable holds when it is given none: 0 for numbers, false for booleans, the empty string for strings, nil for pointers, slices, maps, channels, functions and interfaces, and zeroed fields or elements for structs and arrays.
References
- The Go Programming Language Specification: Variable declarations (The Go Authors)
- The Go Programming Language Specification: Short variable declarations (The Go Authors)
- The Go Programming Language Specification: The zero value (The Go Authors)
- The Go Programming Language Specification: Assignment statements (The Go Authors)
- The Go Programming Language Specification: Declarations and scope (The Go Authors)
- The Go Programming Language Specification: Blank identifier (The Go Authors)
- The Go Programming Language Specification: Package initialization (The Go Authors)
- The Go Programming Language Specification: Map types (The Go Authors)
- Effective Go: Redeclaration and reassignment (The Go Authors)
- Effective Go: Allocation with new (The Go Authors)
- Package strings: Builder (The Go Authors)
- Package strconv: Atoi (The Go Authors)
- Package fmt: Printing (The Go Authors)
- Command vet (The Go Authors)
Report a problem with this lesson
Kept only in this browser. Your Learn progress