Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go examples and idioms [closed]

Tags:

go

Defer statements

A "defer" statement invokes a function whose execution is deferred to the moment the surrounding function returns.

DeferStmt = "defer" Expression .

The expression must be a function or method call. Each time the "defer" statement executes, the parameters to the function call are evaluated and saved anew but the function is not invoked. Deferred function calls are executed in LIFO order immediately before the surrounding function returns, but after the return values, if any, have been evaluated.


lock(l);
defer unlock(l);  // unlocking happens before surrounding function returns

// prints 3 2 1 0 before surrounding function returns
for i := 0; i <= 3; i++ {
    defer fmt.Print(i);
}

Update:

defer is now also the idiomatic way to handle panic in an exception-like manner:

package main

import "fmt"

func main() {
    f()
    fmt.Println("Returned normally from f.")
}

func f() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered in f", r)
        }
    }()
    fmt.Println("Calling g.")
    g(0)
    fmt.Println("Returned normally from g.")
}

func g(i int) {
    if i > 3 {
        fmt.Println("Panicking!")
        panic(fmt.Sprintf("%v", i))
    }
    defer fmt.Println("Defer in g", i)
    fmt.Println("Printing in g", i)
    g(i+1)
}

Go object files actually include a cleartext header:

jurily@jurily ~/workspace/go/euler31 $ 6g euler31.go
jurily@jurily ~/workspace/go/euler31 $ cat euler31.6
amd64
  exports automatically generated from
  euler31.go in package "main"
    import

$$  // exports
  package main
    var main.coin [9]int
    func main.howmany (amount int, max int) (? int)
    func main.main ()
    var main.initdone· uint8
    func main.init ()

$$  // local types
  type main.dsigddd_1·1 struct { ? int }

$$

!
<binary segment>

I have seen a couple of people complaining about the for-loop, along the lines of "why should we have to say i = 0; i < len; i++ in this day and age?".

I disagree, I like the for construct. You can use the long version if you wish, but the idiomatic Go is

var a = []int{1, 2, 3}
for i, v := range a {
    fmt.Println(i, v)
}

The for .. range construct loops over all the elements and supplies two values - the index i and the value v.

range also works on maps and channels.

Still, if you dislike for in any form, you can define each, map etc. in a few lines:

type IntArr []int

// 'each' takes a function argument.
// The function must accept two ints, the index and value,
// and will be called on each element in turn.
func (a IntArr) each(fn func(index, value int)) {
    for i, v := range a {
        fn(i, v)
    }
}

func main() {
    var a = IntArr([]int{2, 0, 0, 9}) // create int slice and cast to IntArr
    var fnPrint = func(i, v int) {
        fmt.Println(i, ":", v)
    } // create a function

    a.each(fnPrint) // call on each element
}

prints

0 : 2
1 : 0
2 : 0
3 : 9

I'm starting to like Go a lot :)


Go and get your stackoverflow reputation

This is a translation of this answer.

package main

import (
    "json"
    "fmt"
    "http"
    "os"
    "strings"
)

func die(message string) {
    fmt.Printf("%s.\n", message);
    os.Exit(1);
}

func main() {
    kinopiko_flair := "https://stackoverflow.com/users/flair/181548.json"
    response, _, err := http.Get(kinopiko_flair)
    if err != nil {
        die(fmt.Sprintf("Error getting %s", kinopiko_flair))
    }

    var nr int
    const buf_size = 0x1000
    buf := make([]byte, buf_size)

    nr, err = response.Body.Read(buf)
    if err != nil && error != os.EOF {
        die(fmt.Sprintf("Error reading response: %s", err.String()))
    }
    if nr >= buf_size { die ("Buffer overrun") }
    response.Body.Close()

    json_text := strings.Split(string(buf), "\000", 2)
    parsed, ok, errtok := json.StringToJson(json_text[0])
    if ! ok {
        die(fmt.Sprintf("Error parsing JSON %s at %s", json_text, errtok))
    }

    fmt.Printf("Your stackoverflow.com reputation is %s\n", parsed.Get ("reputation"))
}

Thanks to Scott Wales for help with .Read ().

This looks fairly clunky still, with the two strings and two buffers, so if any Go experts have advice, let me know.


Here's a nice example of iota from Kinopiko's post:

type ByteSize float64
const (
    _ = iota;   // ignore first value by assigning to blank identifier
    KB ByteSize = 1<<(10*iota)
    MB
    GB
    TB
    PB
    YB
)

// This implicitly repeats to fill in all the values (!)

You can swap variables by parallel assignment:

x, y = y, x

// or in an array
a[j], a[i] = a[i], a[j]

simple but effective.


Here's an idiom from the Effective Go page

switch {
case '0' <= c && c <= '9':
    return c - '0'
case 'a' <= c && c <= 'f':
    return c - 'a' + 10
case 'A' <= c && c <= 'F':
    return c - 'A' + 10
}
return 0

The switch statement switches on true when no expression is given. So this is equivalent to

if '0' <= c && c <= '9' {
    return c - '0'
} else if 'a' <= c && c <= 'f' {
    return c - 'a' + 10
} else if 'A' <= c && c <= 'F' {
    return c - 'A' + 10
}
return 0

At the moment, the switch version looks a little cleaner to me.