エラーで学ぶGolang

2019-10-17

エラーで学ぶReactJS に引き続き、Golangで躓いたことをまとめてみました。

とはいっても完成ではなく、常にWIPとして新たに何かあれば追記していくと思います。

適当に羅列しても良かったんですが流石に長いので雑にカテゴリを区切りました。 量が多いからまだ見づらいかも..

目次

目次

というわけでどうぞ。 間違っている所があればコメントやTwitterで指摘ください。

役に立ったという方は拡散したり私に案件を紹介してくれてもいいんだからね!(未承諾広告)

文字列関連

rune型に一文字以外を指定する

エラー
  • cannot convert 'u0000' (type untyped number) to type string
  • empty character literal or unescaped ' in character literal
  • invalid character literal (more than one character)
  • illegal rune literal

rune は int32 で、 一文字以外の文字列を指定できません。 0文字(空文字) であってもエラーになります。

NG
r := ''
NG
r := 'test'
OK
r := 'a'
OK
var r rune
r = 0

rune型に初期値を指定する場合は 0 を指定しましょう。

参考
go - Cannot assign string with single quote in golang - Stack Overflow

文字列からrune配列を作るときに{}で初期化しようとした

エラー
  • cannot convert "" (type untyped string) to type rune
NG
test = []rune{""}
OK
test = []rune("")
参考

%フォーマットの数に対して引数の数が一致していない

警告
  • Printf format %s reads arg #2, but call has 1 arg
  • Printf call needs 2 args but has 3 args

%フォーマットの数と引数の数が一致しないと警告が発生します。

NG
fmt.Printf("%s %s", "a")
NG
fmt.Printf("%s %s", "a", "b", "c")
OK
fmt.Printf("%s %s", "a", "b")

フォーマット関数の%sが文字列以外を受け取った

警告
  • Sprintf format %s has arg value of wrong type *string
  • Printf format %s has arg value of wrong type *string

PrintfやSprintf の %s は string 型を期待しています。

*string (ポインタ)型であってもそのままでは扱えません。

ポインタが指す値を引数に指定しましょう。

NG
fmt.Sprintf("%s", pointer)
OK
fmt.Sprintf("%s", *pointer)

flagパッケージを使ってコマンドライン引数を受け取る場合、その変数はポインタ型なので注意しましょう。

関数関連

返却値数と左辺の数があっていない

エラー
  • assignment mismatch: 1 variable but test returns 2 values
NG
func test() (int, int) {
   return 1, 2
}

func main() {
   a := test()
}
OK
func test() (int, int) {
   return 1, 2
}

func main() {
   a, b := test()
}

:= での変数定義を関数外で行った

エラー
  • syntax error: non-declaration statement outside function body

関数内でのみ必要な変数は関数内で定義するか、 var で定義しましょう。

NG
v := 1
OK
func Function() {
  v := 1
}
OK
var v = 1

インターフェースに定義されたメソッドが期待通りに登録されていない

エラー
  • cannot use (testStruct1 literal) (value of type testStruct1) as testInterface value in assignment: missing method test
  • cannot use testStruct1 literal (type testStruct1) as type testInterface in assignment:
    • testStruct1 does not implement testInterface (missing test method)
    • testStruct1 does not implement testInterface (wrong type for test method)
      • have test(int) string, want test(int) int
      • have test(string) int, want test(int) int

例えば次のようなインターフェースと変数が定義されているとします。

type testInterface interface {
  test(int) int
}
var i testInterface

これは構造体に test というメソッドが存在することを強制するインタフェースとそれが強制された変数です

NG
type testStruct struct{}
func main() {
    i = testStruct{}
}
NG
type testStruct struct{}
func (t testStruct) test(s string) int {
    return 1
}
func main() {
    i = testStruct{}
}
NG
type testStruct struct{}
func (t testStruct) test(i int) string {
    return "a"
}
func main() {
    i = testStruct{}
}
OK
type testStruct struct{}
func (t testStruct) test(i int) int {
    return 1
}
func main() {
    i = testStruct{}
}

備考

なお、インターフェースではメソッドを定義させないことは強制できません。

関数定義と異なる数の返却値をreturnした

エラー
  • too many arguments to return
  • missing return at end of function

定義した関数の返却数とreturn する値の数があっていなければエラーになります

NG
func test() int {
   fmt.Println("test")
   // 返却値が足りない
}
NG
func test() {
   return 1
}
NG
func test() int {
   return 1, 2
}
OK
func test() int {
   return 1
}
OK
func test() (int, int) {
   return 1, 2
}

返却値が定義されていない関数の返却値を変数に格納した

エラー
  • test() used as value
NG
i := test()
OK
test()

公開されている関数にはコメントが書かれているべき

警告
  • exported function ParseCommand should have comment or be unexported

Golangでは大文字で始まる変数や関数は別パッケージから参照できます。

この状態をエクスポートされた状態(exported) と呼ぶようです。フロントエンドやってる方だとすぐ飲み込めると思います。

上に 関数名[半角スペース]コメント というフォーマットでコメントが書かれて必要があります。

NG
func Test() string {
}
NG
// this is test
func Test() string {
}
OK
// Test this is test
func Test() string {
}

printlnビルトイン関数の中で配列を展開しようとした

エラー
  • invalid use of ... with builtin println

理由はよくわからないんですが、 println 関数の中で配列の展開はできないようです。

変数をデバッグする場合は、 fmt.Println を使いましょう。

NG
println([]string{"a", "b"}...)
OK
fmt.Println([]string{"a", "b"})

defer 文で関数を呼び出していない

エラー
  • function must be invoked in defer statement

defer 文は後処理として呼び出されますが、関数を呼び出し形式で指定する必要があります。

NG
defer 1
NG
defer callback
OK
defer callback()

警告

上記はdefer文のみを書いていますが、本来は関数内に記述されている必要があります。

関数外に記述すると syntax error です syntax error: non-declaration statement outside function body

型関連

インタフェース型を変換できなかった

パニック
  • interface conversion: interface {} is main.Hex, not int
エラー
  • impossible type assertion: int does not implement
  • cannot have dynamic type int

インタフェース型の変数を型アサーションする場合は変数に入れた値と同じ型でなければなりません。 エイリアス型の場合はエイリアス型でキャストしなければエラーになります。

今回は共通で Hex という int のエイリアス型とメソッドが定義されている前提で話を進めます。

type Hex int
func (h Hex) String() string {
      return fmt.Sprintf("%x", int(h))
}
NG
func main() {
        var h interface{} = Hex(100)
        fmt.Println(h.(int))
}
OK
func main() {
        var h interface{} = Hex(100)
        fmt.Println(h.(Hex))
}

なお i, _ := h.(type) のように変数に格納すればパニックは発生しません。 しかし変換できない場合はその型のゼロ値が格納されます。

ビルド時にエラーになるのは、インタフェースのメソッドセットが空でない場合です。

以下のようなインタフェースを用います。

type Stringer interface {
      String() string
}

条件は先程と同様です。

NG
func main() {
        var s Stringer = Hex(100)
        i, _ := s.(int)
        fmt.Println(i)
}
OK
func main() {
        var s Stringer = Hex(100)
        i, _ := s.(Hex)
        fmt.Println(i)
}

宣言時にその型の初期値がゼロ値と同じなら省略すべき

警告
  • should drop = 0 from declaration of var value; it is the zero value
NG
var test int = 0
OK
var test int

少なくとも以下の型のゼロ値は覚えておきましょう。

string:

""

bool:

false

interface{}:

nil (errorもこれ)

ポインタ:

nil

上記以外の intrune 等の数値型は 0 がゼロ値です。

初期値から型が推測できるので、型の指定を省略すべき

警告
  • should omit type int from declaration of var test; it will be inferred from the right-hand side
NG
var num int = 1
OK
var num = 1
OK
num := 1
OK
var num rune = 1

ちなみに rune などの型は値から推測できないので、指定すべきです。

nil を返却することができない

エラー
  • cannot use nil as type SomeStruct in return argument

ポインタやインターフェース型以外が返却値として指定された場合は、 nil を返却することはできません。

構造体の場合は値を指定せずに返却すれば、フィールドには各型のゼロ値が格納されます

NG
type SomeStruct struct {
    Id    integer
    Name  string
}

func Function() SomeStruct {
  return nil
}
OK
type SomeStruct struct {
    Id    integer
    Name  string
}

func Function() SomeStruct {
  return SomeStruct{}
}

== の比較演算子で異なる型の値を比較した

エラー
  • invalid operation: val == nil (mismatched types string and nil)

例えば、数値と nil を比較するとエラーになります。

NG
var val int
if val == nil {
        print(val)
}
OK
var val int
if val == 0 {
        print(val)
}

integer や string のようなプリミティブな変数は定義した時点で各型のゼロ値で初期化されます。 数値の場合は 0 、文字列の場合は ""(空文字) がゼロ値です。

文字列の数値変換をint()で行った

エラー
  • cannot convert "123" (type untyped string) to type int

数値変換は strconv.Atoi 関数で行います。

NG
i := int("123")
OK
i, _ := strconv.Atoi("123")

明示的な変換以外にも、チャネルと異なる型を送信して自動変換された場合にも同じエラーが発生することがあります

NG
ch := make(chan int)
ch <- "abc"
OK
ch := make(chan int)
ch <- 1000

宣言済みの変数に異なる型の値を代入しようとした

エラー
  • cannot use "abc" (type string) as type int in assignment

変数の型を合わせましょう。

NG
var i int
i = "abc"
OK
var i string
i = "abc"
OK
var i int
i = 123

ポインタ型でない変数に ポインタとしてアクセスした

アドレスで指定されるべき引数に * をつけてアクセスすると発生します。

エラー
  • invalid operation: cannot indirect a (variable of type int)
  • invalid indirect of a (type int)

変数をポインタとして渡してあげる場合は & をつけてアドレスを取得しましょう。

NG
a := 1
fmt.Println(*a)
OK
b := 1
a := &b
fmt.Println(*a)
OK
a := &[]int{1, 2}
fmt.Println(*a)

構造体を引数とする場合は省メモリの観点からポインタ型で受け取ることが多いので、 最後の例のように初期化と同時にアドレスを取得するような書き方もできます。

パッケージ関連

package 文にダブルクォートを指定した

エラー
  • expected 'IDENT', found "main"

package の指定にダブルクォートは必要ありません。

NG
package "main"
OK
package main

インポートしたパッケージが存在しない

エラー
  • cannot find package "aaaaa" in any of:

当然ですが存在しないパッケージはインポートできません

NG
import "invalidPackage"
OK
import "validPackage"
OK
import "./local/path/to/package"

ローカルからインポートする場合は、カレントディレクトリからのパス (./) を記述する必要があります

package文が書かれていない

エラー
  • expected 'package', found 'EOF'
  • expected 'package', found 'import'

Goのファイルは必ず一つのパッケージに属するためファイルの先頭にpackage文が書かれている必要があります。

NG
import "fmt"
OK
package main
import "fmt"

import文に文字列以外が指定されている

エラー
  • expected 'STRING', found newline

import文には文字列が指定されている必要があります。

NG
import fmt
OK
import "fmt"

main以外のパッケージは実行できない

エラー
  • cannot run non-main package
  • package test; expected main

go run test.go のようにファイルを実行するとき、 test.go は main パッケージに属している必要があります。

NG
package test
OK
package main

また処理の起点となる main 存在しない場合もエラーになります。

  • runtime.main_main·f: function main is undeclared in the main package

同じディレクトリ内に異なるパッケージに属したファイルが存在する

エラー
  • found packages test1 (test1.go) and test2 (test2.go) in /path/to/test

Golang のインポートはディレクトリ単位で行われます。 そのため、同一ディレクトリ内に異なるパッケージに属するファイルが存在するとエラーになります。

  test/test1.go test/test2.go
NG
package test1
package test2
OK
package test
package test

備考

パッケージ名は統一さえされていれば、ディレクトリ名と一致していなくても構いません

同じ名前のパッケージを再インポートした

エラー
  • test redeclared as imported package name

例えばこのように同じパッケージ名をもつ異なるディレクトリがあるとき

test1/test.go test2/test.go
package test

func Test() {
    println("test1")
}
package test

func Test() {
    println("test2")
}

両方を普通にインポートするとエラーとなります

別名を付けてインポートしてあげましょう。

NG
import (
    "./test1"
    "./test2"
)
OK
import (
    "./test1"
    test2 "./test2"
)

ゴルーチン関連

チャネルの型と違う値を送信しようとした

エラー
  • cannot use i (type string) as type int in send
NG
ch := make(chan int)
ch <- "abc"
OK
ch := make(chan int)
ch <- 1000

チャネルから送信したデータを受け取るべき goroutine が存在しない

(多分)パニック
  • all goroutines are asleep - deadlock!

チャネルからデータを送信するとき、送信側の処理がブロックされ続けてしまうのを防止するランタイムエラーのようです。

以下のようなチャネルとチャネルを引数に取るデータ受け取り用の関数があるとします。

var ch chan int = make(chan int)

func receive(ch <-chan int) {
  fmt.Println(<-ch)
  fmt.Println("received")
}

この関数がチャネルから送信したデータを受け取ってくれないと送信元はいつまで経っても処理を再開できません。 つまり、 receive 関数がゴルーチンとしてデータを待ち受けているかどうかがエラー発生を左右します。

NG
func main() {
    ch <- 123
}
OK
func main() {
    go receive(ch)
    ch <- 123
}

ただし、チャネルからデータを受け取るかどうかに関わらず 一つ以上動作しているゴルーチンがあるとこのランタイムエラーは発生しません。

その他

分類できなかった子達です

参照しようとした変数等が定義されていない

エラー
  • undefined: value
  • undefined: fmt

参照しようとしている変数やパッケージ定義されていないとエラーが発生します

「定義されていないから代入文で宣言しようとしたら発生した」というケースも多いでしょう。

= を使った代入は既存変数への代入を意味します。 ローカル変数の宣言は := を使いましょう。

NG
value = 1
OK
value := 1

定義しているつもりでも 参照されるスコープから外れている場合、 このエラーが発生することがあります。

NG
if test == 1 {
  value := 1
}
print(value) // undefined: value
NG
if value, ok := dict[key]; !ok { // この書き方だと value は if 文の中でのみ有効
  value = 1
}
print(value) // undefined: value
OK
value, ok := dict[key]
if !ok {
  value = 1
}
print(value)

if文に等値条件ではなく代入文を指定した

エラー
  • syntax error: assignment a = 0 used as value
  • expected boolean expression, found assignment (missing parentheses around composite literal?)

よくあるタイポです。

Golangの if文は条件に真偽値のみを指定できます。 また、代入文も値を返却しないのでこのような不正な構文があるとエラーになります。

NG
if a = 1 {
}
OK
if a == 1 {
}

プログラムの構文として不正な文字列が含まれている

エラー
  • invalid character U+0008

これは Goに限った話ではないんですが、プログラムの制御に関係ない文字が入っていると構文エラーになります

Mac の場合 0x08 という不可視の制御文字が入力されてしまうことがあります。(Slackとかでも問題になりましたね..

Macを使う場合は エディタの設定などで乗り切りましょう。もしくはOSを変えるなど。

参考

定数に再代入しようとした

エラー
  • cannot assign to val

const で定義した定数に対して再び値を割り当てようとするとエラーになります。

再代入が予想される場合は var で変数を定義しましょう。

NG
const val = 1
func Function () {
  val = 2
}
OK
var val = 1
func Function () {
  val = 2
}

:= の左辺に定義済みの変数が指定されている

エラー
  • no new variables on left side of :=

:= の左側に新しい変数が定義されていないとエラーになります。 既存変数への代入は = で行うようにしましょう。

NG
value := Function()
value := Function2()
OK
value := Function()
value = Function2()

value, err := f() のように多値が返却される場合、 valueerr のいずれか片方が新規の変数である必要があります。

NG
value, err := Function()
value, err := Function2()
OK
value, err := Function()
value2, err := Function2()

_ (ブランク識別子)のみ値を代入しようとすると同じエラーが発生するようです

func Function() {
    _ := 1
}

_ 変数に代入された値は使用できない

エラー
  • cannot use _ as value
NG
value, _ := Function()
print(_)
OK
value, test := Function()
print(test)

使われていないローカル変数やパッケージがある

エラー
  • value declared and not used
  • imported but not used ...

使わない変数は _ (ブランク識別子)に代入しましょう。

NG
value, err := Function()
OK
value, _ := Function()
OK
import _ "./statik"

備考

最後の例は blank import というらしいです。

実行はされてほしいけどそのファイル内でパッケージが使われない場合に使います。

参考

備考

評価結果が使われない場合もエラーになります。

1 == 1 evaluated but not used

普通こんなことはしませんが、宣言部分だけを削除すると 比較部分だけが取り残されることになりそうです。

関数や構造体の定義が複数行にまたがる場合、行末がカンマか括弧で終わっていない

エラー
  • syntax error: unexpected newline, expecting comma or )
  • syntax error: unexpected newline, expecting comma or }
  • missing ',' before newline in composite literal

構造体の初期化や関数の引数指定で各要素を改行で区切るとき、最終要素が改行で終わっているとこのエラーが発生します。

カンマを追加するか、行末を括弧で終わらせるようにします。

NG
arr := [] int {
        1,
        2
}
OK
arr := [] int {
        1,
        2,
}
OK
arr := [] int {
        1,
        2}
NG
test(
        1,
        2
)
OK
test(
        1,
        2,
)
OK
test(
        1,
        2)

備考

カンマ以外にも + 等の中置き演算子が行末にある場合は継続行と判断されるようです

Golangで長いステートメントを次の行へ継続する - 逆さまにした

ポインタ型変数にnilが入っている状態で参照しようとした

パニック
  • runtime error: invalid memory address or nil pointer dereference
NG
var a *int
fmt.Println(*a)
NG
var a *int
*a = 1
OK
var a *int
b := 1
a = &b
// 以下のように値のアドレスを直接参照することはできない
a = &1 // cannot take the address of 1 (untyped int constant)
a = &nil // cannot take address of nil (untyped nil value)
OK
var b *int = nil
// 同じポインタ型はそのまま格納できる
a = b
// ポインタ型の変数のアドレスは型が異なるため格納できない
a = &b // cannot use &b (value of type **int) as *int value in assignment

配列のインデックスに負値を指定した

エラー
  • invalid slice index -1 (index must be non-negative)

Pythonのように配列の後ろの方から要素を取得するときにマイナスのインデックスを使うことはできません。 配列の要素数からマイナスして要素のインデックスを求めましょう。

NG
a := []int{1, 2, 3}
fmt.Println(a[-1])
OK
a := []int{1, 2, 3}
fmt.Println(len(a)-1)

配列の要素数以上のインデックスを参照した

パニック
  • runtime error: index out of range [3] with length 3

要素数以上のインデックスを指定して要素を取得しようとするとパニックが発生します。

要素数未満のインデックスを指定するか、固定のインデックスを指定するときは要素数のチェックをしましょう。

NG
a := []int{1, 2, 3}
fmt.Println(a[3])
OK
a := []int{1, 2, 3}
fmt.Println(a[2])

range文で得られる2番めの値が使われていないので省略するべき

警告
  • should omit 2nd value from range; this loop is equivalent to for serial := range
NG
for index, _ := range values {
}
OK
for index := range values {
}