package main
import "fmt"
func main() {
message := greetMe("world")
fmt.Println(message)
}
func greetMe(name string) string {
return "Hello, " + name + "!"
}
$ go build
Or try it out in the Go repl, or A Tour of Go.
num := 3 // int
num := 3. // float64
num := 3 + 4i // complex128
num := byte('a') // byte (alias for uint8)
var u uint = 7 // uint (unsigned)
var p float32 = 22.7 // 32-bit float
if day == "sunday" || day == "saturday" {
rest()
} else if day == "monday" && isTired() {
groan()
} else {
work()
}
See: If
if _, err := doThing(); err != nil {
fmt.Println("Uh oh")
}
A condition in an if
statement can be preceded with a statement before a ;
. Variables declared by the statement are only in scope until the end of the if
.
entry := []string{"Jack","John","Jones"}
for i, val := range entry {
fmt.Printf("At position %d, the character %s is present\n", i, val)
}
See: For-Range loops
a, b := getMessage()
func getMessage() (a string, b string) {
return "Hello", "World"
}
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
By defining the return value names in the signature, a return
(no args) will return variables with those names.
See: Named return values
func Hello () {
···
}
Exported names begin with capital letters.
See: Exported names
func main() {
// A "channel"
ch := make(chan string)
// Start concurrent routines
go push("Moe", ch)
go push("Larry", ch)
go push("Curly", ch)
// Read 3 results
// (Since our goroutines are concurrent,
// the order isn't guaranteed!)
fmt.Println(<-ch, <-ch, <-ch)
}
func push(name string, ch chan string) {
msg := "Hey, " + name
ch <- msg
}
Channels are concurrency-safe communication objects, used in goroutines.
See: Goroutines, Channels
ch := make(chan int, 2)
ch <- 1
ch <- 2
ch <- 3
// fatal error:
// all goroutines are asleep - deadlock!
Buffered channels limit the amount of messages it can keep.
See: Buffered channels
ch <- 1
ch <- 2
ch <- 3
close(ch)
for i := range ch {
···
}
ok == false
v, ok := <- ch
See: Range and close
import "sync"
func main() {
var wg sync.WaitGroup
for _, item := range itemList {
// Increment WaitGroup Counter
wg.Add(1)
go doOperation(item)
}
// Wait for goroutines to finish
wg.Wait()
}
func doOperation(item string) {
defer wg.Done()
// do operation on item
// ...
}
A WaitGroup waits for a collection of goroutines to finish. The main goroutine calls Add to set the number of goroutines to wait for. The goroutine calls wg.Done()
when it finishes.
See: WaitGroup
func main() {
defer fmt.Println("Done")
fmt.Println("Working...")
}
Defers running a function until the surrounding function returns.
The arguments are evaluated immediately, but the function call is not ran until later.
func main() {
defer func() {
fmt.Println("Done")
}()
fmt.Println("Working...")
}
Lambdas are better suited for defer blocks.
func main() {
var d = int64(0)
defer func(d *int64) {
fmt.Printf("& %v Unix Sec\n", *d)
}(&d)
fmt.Print("Done ")
d = time.Now().Unix()
}
The defer func uses current value of d, unless we use a pointer to get final value at end of main.
v := Vertex{X: 1, Y: 2}
// Field names can be omitted
v := Vertex{1, 2}
// Y is implicit
v := Vertex{X: 1}
You can also put field names.
v := &Vertex{1, 2}
v.X = 2
Doing v.X
is the same as doing (*v).X
, when v
is a pointer.
func (v *Vertex) Scale(f float64) {
v.X = v.X * f
v.Y = v.Y * f
}
v := Vertex{6, 12}
v.Scale(0.5)
// `v` is updated
By defining your receiver as a pointer (*Vertex
), you can do mutations.
See: Pointer receivers
type Shape interface {
Area() float64
Perimeter() float64
}
type Rectangle struct {
Length, Width float64
}
Struct Rectangle
implicitly implements interface Shape
by implementing all of its methods.
func (r Rectangle) Area() float64 {
return r.Length * r.Width
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Length + r.Width)
}
The methods defined in Shape
are implemented in Rectangle
.
func main() {
var r Shape = Rectangle{Length: 3, Width: 4}
fmt.Printf("Type of r: %T, Area: %v, Perimeter: %v.", r, r.Area(), r.Perimeter())
}