Go syntax
The following list of Go keywords, operators, and snippets are intended as a syntax reference and example of the Go Programming Language.
Keywords
Operators
A list of [most] Go operators and punctuation ordered into approximate precedence with highest priority at the top. Note this is a general overview and may not reflect some of the intricacies of operator and punctuation ordering in Go. Read the spec for a more in depth understanding, https://go.dev/ref/spec. In the context of expressions, most precedence, ambiguity, and readability issues can be resolved with conscientious use of parenthesis.
Types
Inbuilt Go types.
Functions
Inbuilt Go functions.
Examples
Comments
// Single line comment
x := 1 // Inline comment
// Block or multiline comment
//
// The compilier will treat unbroken single line comments
// as if they are a single block
/* Single line comment */
x := 1 /* Inline comment */
/*
Block or multiline comment
*/
// Godoc is the standard tool for compiling comments into
// documentation. I feel the following quote effectively
// communicates comment culture and documentation in Go.
//
// Godoc is conceptually related to Python’s Docstring and Java’s
// Javadoc but its design is simpler. The comments read by godoc
// are not language constructs (as with Docstring) nor must they
// have their own machine-readable syntax (as with Javadoc). Godoc
// comments are just good comments, the sort you would want to read
// even if godoc didn’t exist.
// - Andrew Gerrand, The Go Blog, https://go.dev/blog/godoc
package
// Must be defined only once at the top of each Go file
// Each Go file in a folder must have the same package name
package name
package main // **Special package name**
func main() {
// main package marks the entry package of a program
}
import
// Imports must be defined after the package declaration
// An unused imported package will cause a compiler error
import "fmt" // Standard library
import "path/filepath" // Standard library
import "github.com/pkg/errors" // Custom or third party
fmt.Println("The Go programming language")
errors.Wrap(e, "Alternative example not found")
// Group imports together
import (
"strings"
"path/filepath"
"github.com/pkg/errors"
)
import (
// Alias an import
failure "github.com/pkg/errors"
)
failure.Wrap(e, "Alternative example not found")
import (
// Ignore import
_ "github.com/mattn/go-sqlite3"
)
// This avoids compiler errors on unused imports which is
// desirable when package init functions need to be run but
// no reference is made in code, e.g. database drivers
import (
// Import the package into the current namespace
. "fmt"
)
Println("The Go programming language")
func
func Public() {
// Uppercase 1st letter for package public
}
func private() {
// Lowercase 1st letter for package private
}
func Arguments(s string) {}
Arguments("s")
func Arguments(s string, i int) {}
Arguments("s", 19)
func Arguments(s1, s2, s3 string) {}
Arguments("s1", "s2", "s3")
func Return() string {
return ""
}
s := Return()
func Return() (string, error) {
return "", nil
}
s, e := Return()
func Return() (string, int, error) {
return "", 0, nil
}
s, i, e := Return()
func NamedReturnArgs() (s string, e error) {
s, e = "", nil
return // Return is required
}
s, e := NamedReturnArgs()
func NamedReturnArgs() (s1, s2, string, e error) {
s1, s2, e = "", "", nil
return // Return is required
}
s1, s2, e := NamedReturnArgs()
func NamedFunc() {}
namedFunc := NamedFunc
namedFunc()
anonymousFunc := func() {}
anonymousFunc()
func Variadics(s string, args ...int) {
// Converts arbitrary number of arguments into a slice
// Must be the last defined parameter
}
Variadics("s", 1, 2, 3)
package main
func main() {
// Special function marks the entry point of a program
// Must take no arguments and return no values
}
func init() {
// Special function which is executed after package is imported
// Must take no arguments and return no values
}
var
s := "Go" // Type infered assignment, local variables only
s = "Go" // Reassignment, compile error if s is not already defined
x, y := 1, 2
// Equivlent to:
// x := 1
// y := 2
_ = sendRequest(msg) // Explicitly ignore result
// Unused variables will cause a compiler error
// [Given] func Find(...) (string, bool)
_, found := Find(...) // Ignores `result` string
result, _ := Find(...) // Ignores `found` bool
var s = "Go" // Type inference
var s string // Initialised with zero value
var s string = "Go"
var (
lang = "Go"
x float64 = 18
y float64 = 12.64
)
const
const s1 = "Go" // Type inference
const s2 string = "Go"
const (
ModeRead = "r"
ModeWrite = "w"
ModeExe = "e"
)
const (
// iota keyword creates an incrementing sequence of integers
ModeRead = iota // 0
ModeWrite // 1
ModeExe // 2
)
const (
// Offset iota sequence with an expression
// Must be resolvable at compile time
ModeRead = iota + 1 // 1
ModeWrite // 2
ModeExe // 3
)
const (
// iota sequence is reset for each new const block
ModeRead = iota + 1 // 1
ModeWrite // 2
ModeExe // 3
)
const (
// iota increments even if not used at the start of the block
SpeedNone = 0 // 0
SpeedSlow = iota // 1
SpeedMedium = iota + ((iota - 1) * 2) // 4
SpeedFast // 7
SpeedMax // 10
)
if
if condition {
}
if task.State == "ready" && tasksInProgress < maxTasksInProgress {
task.Start()
tasksInProgress++
}
// Initialisation statement
if e := broadcast(msg); e != nil {
panic(e) // Variables scoped to the block
}
if else
if condition {
} else {
}
if i > 0 {
// positive
} else if i < 0 {
// negative
} else {
// zero
}
// Initialisation statement
if data, e := loadData(url); e != nil {
displayError(e)
} else {
displayData(data) // Variables scoped to the block
}
switch
// `Traditional` case switch
switch i {
case 0:
// Go switches don't fallthrough, i.e. `break` is not needed
case 1:
// ...
default:
}
// Match block, can't mix & match with a traditional switch
switch {
case i > 0:
// positive
case i < 0:
// negative
default:
// zero
}
// These two switches are equivlant
switch ru {
case 'a', 'e', 'i', 'o', 'u': // logical OR
// vowel
default:
// consonant
}
switch ru {
case 'a':
fallthrough // Go switches don't fallthrough without keyword
case 'e':
fallthrough
case 'i':
fallthrough
case 'o':
fallthrough
case 'u':
// vowel
default:
// consonant
}
// Initialisation statement, variables scoped to the block
switch res, e := sendRequest(req); res.Status {
case 200:
// ok
case 400:
// bad request
case 500:
// server error
}
switch status := res.Status; {
case status >= 200 && status < 300:
// ok
case status >= 400 && status < 500:
// bad request
case status >= 500 && status < 600:
// server error
}
for
for {
// Infinite loop
}
for condition {
// While loop
}
for i := 0; i < 5; i++ {
// For loop
}
for i, value := range arrayOrSlice {
// Iterate array or slice
}
for key, value := range myMap {
// Iterate map
}
for indexOrKey := range arraySliceOrMap {
// Only require index or key
}
for _, value := range arraySliceOrMap {
// Only require value
}
for {
// Do while loop #1
if !condition {
break
}
}
for first := true; first || condition; first = false {
// Do while loop #2
}
for drink := range beverages {
if isNonAlcoholic(drink) {
continue
}
consume(drink)
}
defer
defer func() {
// 1. Code deferred to the end of the current function
// 2. It is always executed, even if a panic occurs
}() // <-- 3. Don't forget to call the anonymous function
defer fmt.Println("End of function")
func readFile(filename) {
file, e := os.Open(filename)
if e != nil {
// Handle error...
}
defer file.close()
// Read file...
}
// Deferred function calls are executed in reverse order
defer func() {
fmt.Println("5th")
fmt.Println("Last")
}() // <-- Don't forget to call the anonymous function
defer fmt.Println("4th")
fmt.Println("1st")
defer fmt.Println("3rd")
fmt.Println("2nd")
Slices
Slice creation, access, and reslicing. If you're new to Go I highly recommend reading https://go.dev/blog/slices-intro first.
slice := []int{} // Empty slice #1
slice := make([]int) // Empty slice #2
slice := make([]int, 4) // Pre-sized with length and capcity 4
slice := make([]int, 2, 4) // Pre-sized with length 2 and capcity 4
slice := []int{1, 2, 3} // Initialised, length and capacity 3
n := copy(dst, src) // Copy items from one slice to another
// n is the number of items copied
// n will be the length of the shortest slice
slice = append(slice, 1) // Append single item
slice = append(slice, 1, 2, 3) // Append multiple items
slice = append(slice, otherslice...) // Append another slice
slice = append([]int{0}, slice...) // Prepend single item
slice := slice[from:to] // Reslice a slice
slice := slice[:x] // From 0 to "x"
slice := slice[x:] // From "x" to the end of the slice
slice[i] // Access item
slice[0] // First item
slice[len(slice)-1] // Last item
for i, value := range slice {
// Iterate slice
}
// Pass slice as varadic arguments
numbers := []int{1, 2, 3}
// [Given] func Sum(int...) int
Sum(numbers...)
Arrays
array := [3]int{} // Empty array #1
array := [3]int{0, 0, 0} // Empty array #2
array := [...]int{0, 0, 0} // Empty array #3
array := [3]int{1, 2, 3} // Initialised #1
array := [...]int{1, 2, 3} // Initialised #2
deepCopy := array
slice := array[from:to] // Slice an array
slice := array[:x] // From 0 to "x"
slice := array[x:] // From "x" to the end of the array
slice := array[:] // The whole array
slice = append(slice, array[:]...) // Append an array to a slice
array[i] // Access item
array[0] // First item
array[len(array)-1] // Last item
for i, value := range array {
// Iterate array
}
Maps
var myMap map[int]string // Create map #1
myMap := map[int]string{} // Create map #2
myMap := make(map[int]string) // Create map #3
myMap := &map[int]string{} // Pointer to a new map #1
myMap := new(map[int]string) // Pointer to a new map #2
myMap := map[int]string{
1: "one", // Initialisation
2: "two",
3: "three",
}
_, hasEntry := myMap[key] // Check for entry
value, hasEntry := myMap[key] // Get
myMap[key] = value // Put
for key, value := range myMap {
// Iterate map
}
goto
Goto is an niche yet effective tool for improving the readability and optimising algorithms. It can help simplify excessive nesting of conditionals and loops as well as ease the creation of code generators. However, poor usage of goto can easily complicate things so if in doubt, leave it out. Unfortunatly, goto has been stigmatised to the point that any usage is strongly admonished in many programming communities. Fortunately, the Go authors considered this niche tool important enough to be included in the first release of Go.
goto LABEL
// You will get a compile error if you:
// - try to jump out of a function into another function
// - try to jump over a variable declaration
// - try to jump to a label in a block from outside the block
LABEL:
// ...
for {
// ...
if failed {
goto FAILURE
}
if finished {
goto FINISHED
}
}
FAILURE:
// ...
FINISHED:
// ...
type
type ID int
type shapes []Shape
type Circle struct {
// Field declarations
}
type Shape interface {
// Function declarations
}
type Predicate func() bool
type Load func(file string) ([]byte, error)
type config func(Server) error
type NameList []string
func (nameList NameList) contains(n) bool {
for _, other := range nameList {
if n == other {
return true
}
}
return false
}
names := NameList{"Alice", "Bob", "Charlie"}
names.contains("Bob")
// Type alias
type alias = string
var alias = "Go"
struct
type empty struct {}
type private struct {}
type Public struct {}
type Person struct {
name string
age int
}
p1 := Person{
name: "Oliver"
age: 24
}
p2 := Person{
name: "Oliver"
// age: 0
}
p3 := Person{} // { name: "", age: 0 }
type counter struct {
id string
count int
}
// Pass as pointer, changes to members persist
func (c *counter) increment() {
c.count++
}
// Pass as value, c is a copy of the struct instance
// No changes to the original instance
func (c counter) copy(id string) counter {
c.id = id
return c
}
interface
type empty interface {} // Can represents any value of any type
type private interface {}
type Public interface {}
// Every struct with an 'Area() int' function
// can be represented by the Shape interface
type Shape interface {
Area() int
}
type Canvas interface {
Draw(Shape)
}
type TreeGraph interface {
Depth() int
Children() []Node
ForEachChild(func(Node))
}
type Shape interface {
Area() int
}
type Square struct {
length int
}
func (s *Sqaure) Area() int {
return s.length * s.length
}
var shape Shape = Square{
length: 5,
}
shape.Area() // = 25
Builtin functions
n := len(v) // Length of a string, slice, array, or map
n := cap(v) // Capacity of a slice
// Capacity is the size of the underlying array
// Arrays may also be used as input but their capacity will always
// be equal to their length
array := new([4]string) // Pointer to a new array
slice := make([]string) // Empty slice
slice := make([]string, 4) // Pre-sized with length and capcity 4
slice := make([]string, 2, 4) // Pre-sized with length 2 and capcity 4
slice = append(slice, 1) // Append single item
slice = append(slice, 1, 2, 3) // Append multiple items
slice = append(slice, otherslice...) // Append another slice
slice = append([]int{0}, slice...) // Prepend an item onto a slice
delete(myMap, key) // Removes a key-value pair from a map
n := copy(dst, src) // Copy items from one slice to another
// n is the number of items copied
// n will be the length of the shortest slice
panic(any) // When unable to handle an error
// Akin to throwing in other languages
// Deferred functions are still executed
err := recover() // Akin to catching in other languages
defer func() {
// Only usable within a deferred function call
if err := recover(); err != nil {
// Handle error
}
}()
// Prints arguments to standard output one after the other
print(arg)
print(arg1, arg2, ...)
// With a space between them and then appends a line break
println(arg)
println(arg1, arg2, ...)
go
The go keyword (Goroutine) calls a function asynchronously.
go asyncFunc()
go asyncFunc(a, b int)
go func() {
fmt.Println("Running concurrently")
}()
import (
"fmt"
"sync"
)
func main() {
wg := sync.WaitGroup{}
f := func(s string) {
fmt.Println(s)
wg.Done()
}
wg.Add(2)
go f("Message #1")
go f("Message #2")
wg.Wait() // Waits for wg.Done to be called twice
}
chan
ch := chan int // ch == nil
ch := make(chan int) // Can store 0 items before blocking
ch := make(chan int, 3) // Can store 3 items before blocking
func(ch chan<- int) // Send only
func(ch <-chan int) // Receive only
ch <- 9 // Send
n := <- ch // Receive
n, ok := <- ch // 'ok' true if chan still open
close(ch) // Only senders should close a channel
import (
"fmt"
)
func main() {
input := make(chan int)
output := make(chan int)
go func() {
n := <-input // Wait for input
output <- n * 2
}()
input <- 4
close(input) // Sent items can still be read
n := <-output // Wait for output
fmt.Println(n) // 8
}
import (
"fmt"
)
func main() {
printer := make(chan rune, 3)
done := make(chan bool)
go func() {
for ru, ok := <-printer; ok; ru, ok = <-printer {
fmt.Print(string(ru))
}
fmt.Println("~Printer shutting down~")
close(done)
}()
for _, ru := range "An example of channels in Go.\n" {
printer <- ru
}
close(printer)
<-done // Waits for signal that Goroutine is existing
}
select
// Reads from any channel with an item waiting
select {
case a := <-aChan:
// ...
case b := <-bChan:
// ...
case c := <-cChan:
// ...
default:
// When no inputs waiting on any channel
}
import (
"fmt"
)
func main() {
aliceChan := make(chan string, 1)
bobChan := make(chan string, 1)
charlieChan := make(chan string, 1)
bobChan <- ":)"
select {
case a := <-aliceChan:
fmt.Printf("Alice says '%s'\n", a)
case b := <-bobChan:
fmt.Printf("Bob says '%s'\n", b)
case c := <-charlieChan:
fmt.Printf("Charlie says '%s'\n", c)
default:
fmt.Printf("Nobody said anything\n")
}
}
import (
"fmt"
"time"
)
func main() {
stdLog := make(chan string, 50)
errLog := make(chan string, 50)
exit := make(chan bool)
exiting := make(chan bool)
go runLogger(stdLog, errLog, exit, exiting)
stdLog <- "abc"
stdLog <- "efg"
stdLog <- "xyz"
errLog <- "Bummer!"
time.Sleep(time.Second / 10) // Wait for messages to be processed
close(exit)
<-exiting // Waits for signal that logger func is exiting
}
func runLogger(
stdLog, errLog <-chan string,
exit <-chan bool,
exiting chan<- bool) {
for {
select {
case msg := <-stdLog:
fmt.Println(msg)
case err := <-errLog:
fmt.Printf("ERROR: %s\n", err)
case <-exit:
fmt.Println("~Exiting useless logger~")
close(exiting)
return
}
}
}