goを1.7に上げてから、gocodeが動かなくなったりもしました

go1.6.2 -> go1.7でgocodeが動作しない

手元のmacの環境のgoを1.7に上げたところ、gocodeが上手く動作しなくなった。 (macportsを使っている)

$ sudo port selfupdate
$ sudo port upgrade go
$ go version
go version go1.7 darwin/amd64

以下の様な感じの出力を返す様になってしまった。

$ cat xxx.go | gocode -debug autocomplete /tmp/xxx/xxx.go c89
Found 1 candidates:
  PANIC PANIC PANIC

対応

以下を参考にして、gocodeをcloseした後、インストールしなおしたら直った。

$ gocode close
$ go get -u github.com/nsf/gocode

上手く動作している。

$ cat xxx.go | gocode -debug  autocomplete /tmp/xxx/xxx.go c89
Found 7 candidates:
  func Command(name string, arg ...string) *exec.Cmd
  func CommandContext(ctx context.Context, name string, arg ...string) *exec.Cmd
  func LookPath(file string) (string, error)
  type Cmd struct
  type Error struct
  type ExitError struct
  var ErrNotFound error

memo

/tmp/xxx/xxx.go

package main

import (
    "os/exec"
)

// cancel by context

func main(){
    cmd := exec.//|ここにカーソル
}

deferのタイミングで関数の戻り値を変えたい場合には、named return使うしかないのかも?

はじめに

例えば、以下の様な状況。

  • deferで何らかの後片付けをする。
  • この後片付けのタイミングでerrorが発生。

この時、deferの中の処理で発生したerrorを戻り値として返したい。このような事をしようとした場合にnamed returnを使うしかなかったという話。

sample

f()の後片付けの最中のcleanup()でerrorが発生する場合。

func f()(string, error) {
    s, err := g()
    defer cleanup(s)
    return s, err
}

func g()(string, error) {
    return "ok", nil
}

func cleanup(s string) error {
    return fmt.Errorf("hmm")
}

もちろんこの時の出力結果は以下の様になる。

fmt.Println(f())
// ok, <nil>

deferの中で値を書き換えてもダメ

以下のように書き換えても、やっぱり戻り値はnilになってしまう。

func f()(string, error) {
    s, err := g()
    defer func(){
        if cerr := cleanup(s); err == nil && cerr != nil {
            err = cerr
        }
    }()
    return s, err
}

出力結果

fmt.Println(f())
// ok, <nil>

named returnを使うと大丈夫

named returnは個人的には好きじゃないのでなるべくなら避けたいものだったのだけれど。今回に限っては必要性を認めざるを得ない感じ。

func f()(s string, err error) {
    s, err = g()
    defer func(){
        if cerr := cleanup(s); err == nil && cerr != nil {
            err = cerr
        }
    }()
    return s, err // returnだけでもok
}

今度は発生したerror(hmm)を返す。

fmt.Println(f())
// ok hmm

ところで以下の様に書いてもcompile通っちゃうのが嫌な感じ。

あんまりないことではあるけれど、g()を呼び忘れたような状況。

func f()(s string, err error) {
    // s, err = g()
    defer func(){
        if cerr := cleanup(s); err == nil && cerr != nil {
            err = cerr
        }
    }()
    return s, err
}

zero値で初期化されちゃってるので。

fmt.Println(f())
//  hmm

参考