Go is a systems programming language that first appeared in 2009. Most notably:
For a great introduction and overview of Go, see the Go Programming Language FAQ. One of the answers from the FAQ gives the rationale for the language:
Go is an attempt to combine the ease of programming of an interpreted, dynamically typed language with the efficiency and safety of a statically typed, compiled language. It also aims to be modern, with support for networked and multicore computing. Finally, it is intended to be fast: it should take at most a few seconds to build a large executable on a single computer. To meet these goals required addressing a number of linguistic issues: an expressive but lightweight type system; concurrency and garbage collection; rigid dependency specification; and so on. These cannot be addressed well by libraries or tools; a new language was called for.
Let's start with Hello, world:
package main import "fmt" func main() { fmt.Println("Hello, world") }
To build and run:
$ go run hello.go Hello, world
or you can do it the long way:
$ go build hello.go && ./hello Hello, world
And another little script:
package main import "fmt" import "os" import "strconv" func main() { n, error := strconv.Atoi(os.Args[1]) if error != nil { fmt.Println("A single integer commandline argument is required") } else { a, b := 0, 1 for b <= n { fmt.Println(b) a, b = b, a + b } } }
$ go run fib.go 100 1 1 2 3 5 8 13 21 34 55 89 $ go run fib.go dog A single integer commandline argument is required
And another:
package main import "fmt" func main() { for c := 1; c <= 100; c++ { for b := 1; b <= c; b++ { for a := 1; a <= b; a++ { if a * a + b * b == c * c { fmt.Printf("(%d,%d,%d)\n", a, b, c) } } } } }
Now, here are a few more sample programs.
You should spend the time to take the interactive tour at gloang.org. It is very good.
Also, go through the excellent Go By Example. It isn't interactive like Google's Tour of Go; however, the annotations beside the example code are really nice.
You can also experiment a little using the Go Playground. There's no actual REPL for Go, but things compile so fast it probably doesn't matter too much.
Go programs must run in a package (they start in main
) and there are a bunch of standard
packages that you can import from.
Please browse the list of standard packages to see what's available.
A small example:
package main import ( "fmt" "strings" "time" "math/rand" ) func main() { fmt.Printf("%q\n", strings.Split("a:bcd:ef", ":")) r := rand.New(rand.NewSource(time.Now().UnixNano())) fmt.Println(r.Int31()) }
When writing function signatures supply the argument and argument type(s), and the return type(s). You can name the return value(s) too. Variadic functions are supported, too:
package main import "fmt" func add(x int, y int) int { return x + y } func divide(x, y int) int { return x / y } func swap(x, y string) (string, string) { return y, x } func divmod(x, y int) (quo, rem int) { quo = x / y rem = x % y return } func sumOfFloats(a ...float64) float64 { total := 0.0 for _, x := range a { total += x } return total } func main() { fmt.Println(add(1, 2), divide(33, 10)) one, two := swap("ho", "hi") three, four := divmod(97, 25) fmt.Println(one, two, three, four) fmt.Println(sumOfFloats(8, 4.3, -2, 11.9)) }
Variables are declared with var
. There is some degree of type inference.
There is a default initial value for each type. Inside a function, a short variable
declaration is allowed (but never outside a function).
package main import "fmt" var x int = 10 var y int // value is 0 var z = 12 // type inferred to be int var a, b, c bool // multiple vars declared at once var p, q float64 = 8.9, 2.3 // multiple vars with initializers var ( message = "O noes" start complex128 ) func main() { var s rune = '$' // nothing special here t := "hello" // short var declaration, same as var t string = "hello" fmt.Println(x, y, z, a, b, c, p, q, s, t, message, start) }
Write loops with the for
statement. Learn by example:
for {...}
for x < 10 {...}
for i := 0; i < 10; i++ {...}
for i := range(aSlice) {...}
for i, v := range(aSlice) {...}
for _, v := range(aSlice) {...}
for i, c := range(aString) {...}
for k, v := range(aMap) {...}
for element := range(aChannel) {...}
Note that just as with the if
statement, variables declared at the top of the statement,
before the block, are local to the block.
Just like in C, people:
package main import "fmt" func triple(x int) { x *= 3 // Yep, this is completely useless } func reallyTriple(x *int) { *x *= 3 // Changes the referent of the argument } func main() { x := 3 y := &x // Perfectly okay to point to a local variable triple(x) // x ain't gonna change fmt.Println(x) // Prints 3 reallyTriple(y) // You should draw a picture of what's going on fmt.Println(x) // Yep, prints 9 }
A method is a function with a receiver. Generally the receiver is a pointer but doesn't have to be. By making it a pointer you allow the method to be a mutator, and you don't incur cost of a copy. TODO
An interface is a collection of method signatures. You don't have to say that a type impements an interface, the compile will check that all the methods are implemented when you try to assign (or pass) an object to a variable that is given an interface for a type.
package main import ( "fmt" "math" ) type Shape interface { perimeter() float64 area() float64 } type Circle struct { radius float64 } func (c Circle) perimeter() float64 { return 2.0 * math.Pi * c.radius } func (c Circle) area() float64 { return math.Pi * c.radius * c.radius } type Rectangle struct { length, width float64 } func (r Rectangle) perimeter() float64 { return 2.0 * (r.width + r.length) } func (r Rectangle) area() float64 { return r.width * r.length } func showDetails(s Shape) { fmt.Printf("%T perimeter is %g and area is %g\n", s, s.perimeter(), s.area()) } func main() { showDetails(Rectangle{5.5, 20.3}) showDetails(Circle{10}) }
$ go run shapes.go main.Rectangle perimeter is 51.6 and area is 111.65 main.Circle perimeter is 62.83185307179586 and area is 314.1592653589793
No asserts, strangely enough. Anyway, here is a pacakge to test:
package stats import "errors" func Average(a []float64) (float64, error) { if len(a) == 0 { return 0, errors.New("No average for empty collection") } total := 0.0 for _, x := range a { total += x } return total / float64(len(a)), nil }
And here's the test.
package stats import "testing" func TestAverage(t *testing.T) { average, error := Average([]float64{10.0, 1.0, 4.0}) if average != 5.0 && error != nil { t.Errorf("Average of [10, 1, 4] should be 5") } _, error = Average([]float64{}) if error == nil { t.Errorf("Average of empty slice should return an error") } }
The code and the test should be the only two things in the directory. Then do:
$ go test PASS ok _/Users/ray/w/code/go/stats 0.040s
This works, and is good enough for school, but in general, testing should be part of building your applications according to the standard Go project layout, which you'll learn about in the video in the next section.
Here is how you can build an app using your own packages.
Here's some interesting technical items from the Go Programming Language Specification:
break
, continue
, fallthrough
, return
,
++
, --
, )
, ]
, or }
.
break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var
bool byte complex64 complex128 error float32 float64 int int8 int16 int32 int64 rune string uint uint8 uint16 uint32 uint64 uintptr true false iota nil append cap close complex copy delete imag len make new panic print println real recover
Operators | Associativity | Description |
---|---|---|
+ - ^ !
| — | unary plus, negation, bitwise complement, logical not |
* / % << >> & &^
| L | multiplication, division, modulo, left shift, right shift, binary and, binary and-not |
+ - | ^
| L | addition, subtraction, binary or, binary xor |
== != < <= > >=
| L | equals, not equals, less, less or equal, greater, greater or equal |
&&
| L | short-circuit and |
||
| L | short-circuit or |
Type | Description |
---|---|
bool | the boolean type containing only the values true
and false
|
uint8 | unsigned 8-bit integers (0 to 255) |
uint16 | unsigned 16-bit integers (0 to 65535) |
uint32 | unsigned 32-bit integers (0 to 4294967295) |
uint64 | unsigned 64-bit integers (0 to 18446744073709551615) |
int8 | signed 8-bit integers (-128 to 127) |
int16 | signed 16-bit integers (-32768 to 32767) |
int32 | signed 32-bit integers (-2147483648 to 2147483647) |
int64 | signed 64-bit integers (-9223372036854775808 to 9223372036854775807) |
float32 | IEEE-754 32-bit floating-point numbers |
float64 | IEEE-754 64-bit floating-point numbers |
complex64 | complex numbers with float32 real and imaginary parts
|
complex128 | complex numbers with float64 real and imaginary parts
|
byte | alias for uint8
|
rune | alias for int32
|
uint | IMPLEMENTATION-SPECIFIC either 32 or 64 bits |
int | IMPLEMENTATION-SPECIFIC same size as uint
|
uintptr | IMPLEMENTATION-SPECIFIC an unsigned integer large enough to store the uninterpreted bits of a pointer value |
string | immutable sequences of bytes |
error | type error interface {Error() string}
|
Kind of type | Examples |
---|---|
Array Types | [7]int
|
Slice Types | []int
|
Struct Types | struct {X int; Y int}
|
Map Types | map[string]int
|
Function Types | func(int)int
|
Channel Types | chan int
|
Pointer Types | *int
|
Interface Types |
|