【初級編】Go言語を始める方のための落とし穴、問題の解決方法やよくある間違い

Shinichi Jufuku
Feb 8 · 37 min read

概要

・開始の中括弧を別の行に配置することはできません

package mainimport "fmt"func main()  
{ //error, can't have the opening brace on a separate line
fmt.Println("hello there!")
}
package mainimport "fmt"func main() {  
fmt.Println("works!")
}

・変数の未使用

package mainvar gvar int //not an errorfunc main() {  
var one int //error, unused variable
two := 2 //error, unused variable
var three int //error, even though it's assigned 3 on the next line
three = 3
func(unused string) {
fmt.Println("Unused arg. No compile error")
}("what?")
}
package mainimport "fmt"func main() {  
var one int
_ = one
two := 2
fmt.Println(two)
var three int
three = 3
one = three
var four int
four = four
}

・未使用のimport

package mainimport (  
"fmt"
"log"
"time"
)
func main() {
}
package mainimport (  
_ "fmt"
"log"
"time"
)
var _ = log.Printlnfunc main() {
_ = time.Now
}

・ 短い変数宣言は関数内でのみ使用できます

package mainmyvar := 1 //errorfunc main() {  
}
package mainvar myvar = 1func main() {  
}

・短い変数宣言を使用した変数の再宣言

package mainfunc main() {  
one := 0
one := 1 //error
}
package mainfunc main() {  
one := 0
one, two := 1,2
one,two = two,one
}

・短い変数宣言を使用してフィールド値を設定することはできません

package mainimport (  
"fmt"
)
type info struct {
result int
}
func work() (int,error) {
return 13,nil
}
func main() {
var data info
data.result, err := work() //error
fmt.Printf("info: %+v\n",data)
}
package mainimport (  
"fmt"
)
type info struct {
result int
}
func work() (int,error) {
return 13,nil
}
func main() {
var data info
var err error
data.result, err = work() //ok
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("info: %+v\n",data) //prints: info: {result:13}
}

・偶発的変数シャドウイング

package mainimport "fmt"func main() {  
x := 1
fmt.Println(x) //prints 1
{
fmt.Println(x) //prints 1
x := 2
fmt.Println(x) //prints 2
}
fmt.Println(x) //prints 1 (bad if you need 2)
}

・明示的な型なしで変数を初期化するために “nil”を使用することはできません

package mainfunc main() {  
var x = nil //error
_ = x
}
package mainfunc main() {  
var x interface{} = nil
_ = x
}

・”nil”スライスとマップを使う

package mainfunc main() {  
var m map[string]int
m["one"] = 1 //error
}
package mainfunc main() {  
var s []int
s = append(s,1)
}

・Mapのcapacity

package mainfunc main() {  
m := make(map[string]int,99)
cap(m) //error
}

・文字列を “nil”にすることはできません

package mainfunc main() {  
var x string = nil //error
if x == nil { //error
x = "default"
}
}
package mainfunc main() {  
var x string //defaults to "" (zero value)
if x == "" {
x = "default"
}
}

・関数引数に配列

package mainimport "fmt"func main() {  
x := [3]int{1,2,3}
func(arr [3]int) {
arr[0] = 7
fmt.Println(arr) //prints [7 2 3]
}(x)
fmt.Println(x) //prints [1 2 3] (not ok if you need [7 2 3])
}
package mainimport "fmt"func main() {  
x := [3]int{1,2,3}
func(arr *[3]int) {
(*arr)[0] = 7
fmt.Println(arr) //prints &[7 2 3]
}(&x)
fmt.Println(x) //prints [7 2 3]
}
package mainimport "fmt"func main() {  
x := []int{1,2,3}
func(arr []int) {
arr[0] = 7
fmt.Println(arr) //prints [7 2 3]
}(x)
fmt.Println(x) //prints [7 2 3]
}

・スライスと配列の “range”句の予期しない値

package mainimport "fmt"func main() {  
x := []string{"a","b","c"}
for v := range x {
fmt.Println(v) //prints 0, 1, 2
}
}
package mainimport "fmt"func main() {  
x := []string{"a","b","c"}
for _, v := range x {
fmt.Println(v) //prints a, b, c
}
}

・スライスと配列は一次元です

package mainfunc main() {  
x := 2
y := 4
table := make([][]int,x)
for i:= range table {
table[i] = make([]int,y)
}
}
package mainimport "fmt"func main() {  
h, w := 2, 4
raw := make([]int,h*w)
for i := range raw {
raw[i] = i
}
fmt.Println(raw,&raw[4])
//prints: [0 1 2 3 4 5 6 7] <ptr_addr_x>
table := make([][]int,h)
for i:= range table {
table[i] = raw[i*w:i*w + w]
}
fmt.Println(table,&table[1][0])
//prints: [[0 1 2 3] [4 5 6 7]] <ptr_addr_x>
}

・存在しないマップキーへのアクセス

package mainimport "fmt"func main() {  
x := map[string]string{"one":"a","two":"","three":"c"}
if v := x["two"]; v == "" { //incorrect
fmt.Println("no entry")
}
}
package mainimport "fmt"func main() {  
x := map[string]string{"one":"a","two":"","three":"c"}
if _,ok := x["two"]; !ok {
fmt.Println("no entry")
}
}

・文字列は不変です

package mainimport "fmt"func main() {  
x := "text"
x[0] = 'T'
fmt.Println(x)
}
package mainimport "fmt"func main() {  
x := "text"
xbytes := []byte(x)
xbytes[0] = 'T'
fmt.Println(string(xbytes)) //prints Text
}

・文字列とバイトスライス間の変換

・文字列とインデックス演算子

package mainimport "fmt"func main() {  
x := "text"
fmt.Println(x[0]) //print 116
fmt.Printf("%T",x[0]) //prints uint8
}

・文字列がUTF-8テキストとは限らない

package mainimport (  
"fmt"
"unicode/utf8"
)
func main() {
data1 := "ABC"
fmt.Println(utf8.ValidString(data1)) //prints: true
data2 := "A\xfeC"
fmt.Println(utf8.ValidString(data2)) //prints: false
}

・文字列の長さ

data = u'♥'  
print(len(data)) #prints: 1
package mainimport "fmt"func main() {  
data := "♥"
fmt.Println(len(data)) //prints: 3
}
package mainimport (  
"fmt"
"unicode/utf8"
)
func main() {
data := "♥"
fmt.Println(utf8.RuneCountInString(data)) //prints: 1
}
package mainimport (  
"fmt"
"unicode/utf8"
)
func main() {
data := "é"
fmt.Println(len(data)) //prints: 3
fmt.Println(utf8.RuneCountInString(data)) //prints: 2
}

・複数行のスライス、配列、およびマップリテラルにコンマがない

package mainfunc main() {  
x := []int{
1,
2 //error
}
_ = x
}
package mainfunc main() {  
x := []int{
1,
2,
}
x = x
y := []int{3,4,} //no error
y = y
}

・log.Fatalとlog.Panicはログ出力以上のことをする

package mainimport "log"func main() {  
log.Fatalln("Fatal Level: log entry") //app exits here
log.Println("Normal Level: log entry")
}

・組み込みデータ構造操作が同期されていない

・”range”句の文字列の繰り返し値

package mainimport "fmt"func main() {  
data := "A\xfe\x02\xff\x04"
for _,v := range data {
fmt.Printf("%#x ",v)
}
//prints: 0x41 0xfffd 0x2 0xfffd 0x4 (not ok)
fmt.Println()
for _,v := range []byte(data) {
fmt.Printf("%#x ",v)
}
//prints: 0x41 0xfe 0x2 0xff 0x4 (good)
}

・”for range”句を使用してマップを反復する

package mainimport "fmt"func main() {  
m := map[string]int{"one":1,"two":2,"three":3,"four":4}
for k,v := range m {
fmt.Println(k,v)
}
}

・「switch」ステートメントでのフォールスルー動作

package mainimport "fmt"func main() {  
isSpace := func(ch byte) bool {
switch(ch) {
case ' ': //error
case '\t':
return true
}
return false
}
fmt.Println(isSpace('\t')) //prints true (ok)
fmt.Println(isSpace(' ')) //prints false (not ok)
}
package mainimport "fmt"func main() {  
isSpace := func(ch byte) bool {
switch(ch) {
case ' ', '\t':
return true
}
return false
}
fmt.Println(isSpace('\t')) //prints true (ok)
fmt.Println(isSpace(' ')) //prints true (ok)
}

・インクリメントとデクリメント

package mainimport "fmt"func main() {  
data := []int{1,2,3}
i := 0
++i //error
fmt.Println(data[i++]) //error
}
package mainimport "fmt"func main() {  
data := []int{1,2,3}
i := 0
i++
fmt.Println(data[i])
}

・ビットごとのNOT演算子

package mainimport "fmt"func main() {  
fmt.Println(~2) //error
}
package mainimport "fmt"func main() {  
var d uint8 = 2
fmt.Printf("%08b\n",^d)
}
package mainimport "fmt"func main() {  
var a uint8 = 0x82
var b uint8 = 0x02
fmt.Printf("%08b [A]\n",a)
fmt.Printf("%08b [B]\n",b)
fmt.Printf("%08b (NOT B)\n",^b)
fmt.Printf("%08b ^ %08b = %08b [B XOR 0xff]\n",b,0xff,b ^ 0xff)
fmt.Printf("%08b ^ %08b = %08b [A XOR B]\n",a,b,a ^ b)
fmt.Printf("%08b & %08b = %08b [A AND B]\n",a,b,a & b)
fmt.Printf("%08b &^%08b = %08b [A 'AND NOT' B]\n",a,b,a &^ b)
fmt.Printf("%08b&(^%08b)= %08b [A AND (NOT B)]\n",a,b,a & (^b))
}

・演算子の優先順位の違い

package mainimport "fmt"func main() {  
fmt.Printf("0x2 & 0x2 + 0x4 -> %#x\n",0x2 & 0x2 + 0x4)
//prints: 0x2 & 0x2 + 0x4 -> 0x6
//Go: (0x2 & 0x2) + 0x4
//C++: 0x2 & (0x2 + 0x4) -> 0x2
fmt.Printf("0x2 + 0x2 << 0x1 -> %#x\n",0x2 + 0x2 << 0x1)
//prints: 0x2 + 0x2 << 0x1 -> 0x6
//Go: 0x2 + (0x2 << 0x1)
//C++: (0x2 + 0x2) << 0x1 -> 0x8
fmt.Printf("0xf | 0x2 ^ 0x2 -> %#x\n",0xf | 0x2 ^ 0x2)
//prints: 0xf | 0x2 ^ 0x2 -> 0xd
//Go: (0xf | 0x2) ^ 0x2
//C++: 0xf | (0x2 ^ 0x2) -> 0xf
}

・未エクスポートの構造体フィールドはエンコードされません

package mainimport (  
"fmt"
"encoding/json"
)
type MyData struct {
One int
two string
}
func main() {
in := MyData{1,"two"}
fmt.Printf("%#v\n",in) //prints main.MyData{One:1, two:"two"}
encoded,_ := json.Marshal(in)
fmt.Println(string(encoded)) //prints {"One":1}
var out MyData
json.Unmarshal(encoded,&out)
fmt.Printf("%#v\n",out) //prints main.MyData{One:1, two:""}
}

・アクティブなゴルーチンでアプリが終了する

package mainimport (  
"fmt"
"time"
)
func main() {
workerCount := 2
for i := 0; i < workerCount; i++ {
go doit(i)
}
time.Sleep(1 * time.Second)
fmt.Println("all done!")
}
func doit(workerId int) {
fmt.Printf("[%v] is running\n",workerId)
time.Sleep(3 * time.Second)
fmt.Printf("[%v] is done\n",workerId)
}
package mainimport (  
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
done := make(chan struct{})
workerCount := 2
for i := 0; i < workerCount; i++ {
wg.Add(1)
go doit(i,done,wg)
}
close(done)
wg.Wait()
fmt.Println("all done!")
}
func doit(workerId int,done <-chan struct{},wg sync.WaitGroup) {
fmt.Printf("[%v] is running\n",workerId)
defer wg.Done()
<- done
fmt.Printf("[%v] is done\n",workerId)
}
package mainimport (  
"fmt"
"sync"
)
func main() {
var wg sync.WaitGroup
done := make(chan struct{})
wq := make(chan interface{})
workerCount := 2
for i := 0; i < workerCount; i++ {
wg.Add(1)
go doit(i,wq,done,&wg)
}
for i := 0; i < workerCount; i++ {
wq <- i
}
close(done)
wg.Wait()
fmt.Println("all done!")
}
func doit(workerId int, wq <-chan interface{},done <-chan struct{},wg *sync.WaitGroup) {
fmt.Printf("[%v] is running\n",workerId)
defer wg.Done()
for {
select {
case m := <- wq:
fmt.Printf("[%v] m => %v\n",workerId,m)
case <- done:
fmt.Printf("[%v] is done\n",workerId)
return
}
}
}

・バッファリングされていないチャネルへの送信は、ターゲットレシーバが準備完了になるとすぐに戻る

package mainimport "fmt"func main() {  
ch := make(chan string)
go func() {
for m := range ch {
fmt.Println("processed:",m)
}
}()
ch <- "cmd.1"
ch <- "cmd.2" //won't be processed
}

・閉じたチャンネルに送信するとパニックが発生する

package mainimport (  
"fmt"
"time"
)
func main() {
ch := make(chan int)
for i := 0; i < 3; i++ {
go func(idx int) {
ch <- (idx + 1) * 2
}(i)
}
//get the first result
fmt.Println(<-ch)
close(ch) //not ok (you still have other senders)
//do other work
time.Sleep(2 * time.Second)
}
package mainimport (  
"fmt"
"time"
)
func main() {
ch := make(chan int)
done := make(chan struct{})
for i := 0; i < 3; i++ {
go func(idx int) {
select {
case ch <- (idx + 1) * 2: fmt.Println(idx,"sent result")
case <- done: fmt.Println(idx,"exiting")
}
}(i)
}
//get first result
fmt.Println("result:",<-ch)
close(done)
//do other work
time.Sleep(3 * time.Second)
}

・”nil”チャンネルを使う

package mainimport (  
"fmt"
"time"
)
func main() {
var ch chan int
for i := 0; i < 3; i++ {
go func(idx int) {
ch <- (idx + 1) * 2
}(i)
}
//get first result
fmt.Println("result:",<-ch)
//do other work
time.Sleep(2 * time.Second)
}
package mainimport "fmt"  
import "time"
func main() {
inch := make(chan int)
outch := make(chan int)
go func() {
var in <- chan int = inch
var out chan <- int
var val int
for {
select {
case out <- val:
out = nil
in = inch
case val = <- in:
out = outch
in = nil
}
}
}()
go func() {
for r := range outch {
fmt.Println("result:",r)
}
}()
time.Sleep(0)
inch <- 1
inch <- 2
time.Sleep(3 * time.Second)
}

・値を受け取る側のメソッドは元の値を変更できない

package mainimport "fmt"type data struct {  
num int
key *string
items map[string]bool
}
func (this *data) pmethod() {
this.num = 7
}
func (this data) vmethod() {
this.num = 8
*this.key = "v.key"
this.items["vmethod"] = true
}
func main() {
key := "key.1"
d := data{1,&key,make(map[string]bool)}
fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)
//prints num=1 key=key.1 items=map[]
d.pmethod()
fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)
//prints num=7 key=key.1 items=map[]
d.vmethod()
fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)
//prints num=7 key=v.key items=map[vmethod:true]
}

終わりに

Eureka Engineering

Learn about Eureka’s engineering efforts, product developments and more.

Shinichi Jufuku

Written by

API Team at eureka, inc.

Eureka Engineering

Learn about Eureka’s engineering efforts, product developments and more.