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

package
import
func
return
type
map
chan
struct
interface
var
const
defer
if
else
switch
case
default
fallthrough
for
range
break
continue
goto
go
select

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.

( )
{ }
[ ]
->
.
* (unary)
+ (unary)
- (unary)
! (unary)
~ (unary)
++ (unary)
-- (unary)
*
/
%
<<
>>
&
&^
+
-
|
^
==
!=
<
<=
>
>=
&&
||
=
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=

Types

Inbuilt Go types.

bool
string
byte
rune
int
int8
int16
int32
int64
uint
uint8
uint16
uint32
uint64
uintptr
float32
float64
complex64
complex128

Functions

Inbuilt Go functions.

len
cap
new
make
append
delete
copy
panic
recover
print
println

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 
		} 
	} 
}
Back to examples menu