Go: getting oriented
Contents
- Hello world
- Tools
- File format and syntax
- Assertions
- Variable declarations
- Strings
for
loop- I/O
- Network I/O
- System calls
- Data structures: map
- Timing
- Testing and benchmarking
- Sources
Hello world
package main
import "fmt"
func main() {
fmt.Println("Hello đ")
}
Tools
- I installed MacOS Homebrew golang
go build myprogram.go
to compile and linkgo run myprogram.go
to compile, link and rungo fmt
to format code (alphabetizes import declarations)go vet
to check codego test
go test -bench
for benchmarking- Remote packages:
go get
to install (-v
for verbose,-u
to update)- Mine (MacOS) installs in
~/go
by default- This represents default
$GOPATH
?
- This represents default
- Therefore add
$HOME/go/bin
to your shell$PATH
- Mine (MacOS) installs in
go list ...
to list all installed packages
- Tools not built in:
golang.org/x/tools/cmd/goimports
updates all import statements, adding needed or removing unused
- Playground: https://play.golang.org/
- Currently maintained REPLs Iâve tried (as of 2019-05-08)
- gomacro https://github.com/cosmos72/gomacro
- My preference. Fast; has integrated debugger
- gore https://github.com/motemen/gore was too slow for me
- go-pry https://github.com/d4l3k/go-pry - I did not try it
- gomacro https://github.com/cosmos72/gomacro
File format and syntax
- No semicolons
- Comments are
//
and/* ... */
if
andfor
conditions are not parenthesized+
is overloaded for string concatenation+=
is valid and has all the usual variants- Go has postfix only
++
and--
- these are statements, not expressions as in C, so
j = i++
is invalid
- these are statements, not expressions as in C, so
Assertions
Not provided. Use if <failing_cond> { panic("failed") }
instead.
Variable declarations
Use :=
operator to initialize, =
to assign.
The following four ways to declare and initialize variables are equivalent.
In practice, you should generally use one of the first two forms, with explicit initialization to say that the initial value is important and implicit initialization to say that the initial value doesnât matter. âDonovan and Kernighan, The Go Programming Language 7
First: short variable declaration. Declare and initialize without var
keyword or type, using :=
. This form may only be used within a
function.
a := "foo"
if a != "foo" { panic("failed") }
Second: declare with type. Here, is default-initialized with empty string.
var b string
if b != "" { panic("failed") }
Assign another value using =
operator.
b = "bar"
if b != "bar" { panic("failed") }
Third: declare and initialize with a value, using =
.
var c string = "baz"
if c != "baz" { panic("failed") }
Fourth: declare and initialize without type, using =
. âRarely used
except when declaring multiple variablesâ (Donovan and Kernighan,
The Go Programming Language 7).
var d = "qux"
if d != "qux" { panic("failed") }
Strings
import "strings"
- split:
strings.Split(mystring, "\n")
- join:
strings.Join(arr, " ")
for
loop
- Syntax
for <init>; <cond>; <post> {}
- No parentheses
- All three loop statements are optional
- While loop is written as
for <cond> {}
- Infinite loop:
for {}
- For [FIXME: iterator], just use e.g.
for input.Scan() { /* do something with
input*/ }
Range for loop (enumeration) uses for idx, elt := range arr
. Go prohibits
unused local variables, so if youâre not going to use the index variable, the
convention is to use the blank identifier _
:
func echo() {
s, sep := "", ""
for _, arg := range os.Args[1:] {
s += sep + arg
sep = " "
}
fmt.Println(s)
}
Rather than use the blank identifier for the value variable, you can simply omit it:
for idx := range arr
is equivalent to for idx, _ := range arr
.
Rather than string concatenation, use strings.Join
:
func echo() {
fmt.Println(strings.Join(os.Args[1:], " "))
}
I/O
fmt.Println()
fmt.Printf()
with e.g.%0.2f
to format a float to two decimal points of precisionfmt.Fprintf()
â TODOos.Args
is an array of command-line arguments- Use
bufio.Scanner()
,io/ioutil.ReadFile()
, andio/ioutil.WriteFile()
(they are implemented using*os.File.Read()
and*os.File.Write()
, which rarely need to be used) - stdin
input.Scan()
- removes newline
- returns
true
if there is a line - returns
false
if there is no more input
- get stdin:
input := bufio.NewScanner(os.Stdin)
for input.Scan() { /* do something with input.Text() */ }
- read file data in streaming mode
os.Open()
returns (a) a file handle and (b) an error value- read file data:
file, err := os.Open("./data.txt")
- Check
err
. If itâsnil
, all is well. data := bufio.NewScanner(file)
for data.Scan() { /* do something with data.Text() */ }
- Close the file with
file.Close()
.
- read file data in one operation, using
io/ioutil.ReadFile()
io/ioutil.ReadFile()
returns (a) a byte slice and (b) error value- read file data:
data, err := ioutil.ReadFile("./data.txt")
- Check
err
- Do something with
data
: e.g. process it as newline-terminated strings:strdata := strings.Split(string(data), "\n")
for _, line := range strdata { /* do something with
line*/ }
Read file data in streaming mode (checks file error only, not scanner error):
file, err := os.Open("./data.txt")
if err != nil {
fmt.Println("Error")
return
}
data := bufio.NewScanner(file)
for data.Scan() {
fmt.Println(data.Text())
}
Read file data in one operation (omits error checking):
data, err := ioutil.ReadFile("./data.txt")
strdata := strings.Split(string(data), "\n")
for _, line := range strdata {
fmt.Println(line)
}
Network I/O
net/http.Get(url
) returnsresp, err
;resp
is a struct,resp.Body
is a readable streamio/ioutil.ReadAll(resp.Body)
returnsbody, err
System calls
os.Exit()
os.Stderr
Data structures: map
- Create with
make
, e.g.make(map[string]int)
initializes a map with string keys and integer values, with keys set to zero values of""
and values set to zero values of 0. - Order of map iteration is not specified and in practice is random.
Creating, adding elements to, and iterating over a map:
counts := make(map[string]int)
counts["foo"]++
counts["bar"]++
counts["bar"]++
counts["baz"]++
counts["baz"]++
counts["baz"]++
for elt, idx := range counts {
fmt.Println(elt, idx)
}
Output (order not guaranteed):
foo 1
bar 2
baz 3
Taking standard input:
counts := make(map[string]int)
input := bufio.NewScanner(os.Stdin)
for input.Scan() {
counts[input.Text()]++
}
for elt, idx := range counts {
fmt.Println(elt, idx)
}
Timing
start := time.Now()
âŚtime.Since(start).Seconds()
Testing and benchmarking
- Test functions and benchmarking functions go in
<libname>_test.go
import "testing"
- Use
go test
andgo test -bench
Writing a benchmark function for some function f()
:
func BenchmarkF1(b *testing.B) {
for i := 0; i < b.N; i++ {
f1()
}
}
Run with go test -bench=args1
.
Sources
Alan A. A. Donovan and Brian W. Kernighan, The Go Programming Language. Addison-Wesley, 2016, Chapter 1: Tutorial