Go言語に特有な部分にフォーカスしてつらつらと書いていきます。
変数宣言の省略
number := 100 // var int number = 100
※使っていない変数があるとエラーをはく
複数のパッケージ名・変数宣言
()
で囲う
import (
“fmt”
“math”
)
const (
Small = 1
Medium = Small << 10
Large = Medium << 10
)
slice
要素数を書くと配列、書かないとスライスになる。
// array
ary := [...]int{1, 3, 5, 7, 10}
// slice
s := []int{1, 3, 5, 7, 10}
基本的にはスライスの方が多く使われる。
スライスは append()
や copy()
が使える。
ハッシュ中の値の有無
// map = 連想配列、ハッシュ
m := map[string]int{
"sato": 100,
"suzuki": 200}
value, ok := m["sato"]
fmt.Println(value, ok) // 100 true 存在しない場合は 0 false
制御文の中に変数を書ける
if n := 100; n > 50 {
fmt.Println("Good!!")
}
fmt.Println(n) // undefined
変数のスコープは違うが、以下と同じ意味
n := 100
if n > 50 {
fmt.Println("Good!!")
}
fmt.Println(n) // 100
forの省略
// 初期化と処理後の処理の省略が可能
i := 0
// whileと同じ処理になる(Go言語にはwhileがない)
for i < 10 {
i++
}
for {
// 処理
}
range
スライス、マップなどを要素数分ループさせるキーワード。
s := []string{"red", "blue", "yellow"}
// '_'はワイルドカード
for _, value := range s {
fmt.Println(value)
}
m := map[string]int{
"sato": 100,
"suzuki": 200}
for k, value := range m {
fmt.Println(k, value)
}
メソッドとレシーバ
メソッド:構造体などのデータ型に紐づいた関数
レシーバ:メソッドを呼び出される対象
type user struct {
name string
score int
}
// 値渡し
func (u user) show() {
fmt.Println(u.name, u.score)
}
// ポインタ渡し
func (u *user) add() {
// 元の値を書き換える
// ポインタ変数にしないとscore=10のまま
u.score++
}
func main() {
u := user{
name: "sato",
score: 10,
}
u.add()
u.show()
}
空のインターフェース
空のインターフェースを利用すると、
あらゆる型を受け付ける関数をつくることができる。
一方、型の動的チェックをするために、
if文の「型アサーション」と、
switch文の「型switch」が提供されている。
// あらゆる型を受け付ける関数
func anyFunc(any interface{}) {
// 型アサーション
if _, ok := any.(string); ok {
fmt.Println("this is string")
} else {
fmt.Println("this is not string")
}
// 型switch
switch any.(type) {
case string:
fmt.Println("this is string")
default:
fmt.Println("this is not string")
}
}
func main() {
anyFunc("空のインターフェース")
}
goroutine
並行処理(順不同に行う処理)を実現する
func task1() {
// 重い処理の想定で1秒待つ
time.Sleep(time.Second)
fmt.Println("task1 finished...")
}
func task2() {
fmt.Println("task2 finished...")
}
func main() {
go task1()
go task2()
// 上記関数が終わる前にmain関数が終わってしまうのを防ぐ
time.Sleep(time.Second * 3)
}
task1()、task2()が走り、
すぐにtask2()が終わり、1秒後にtask1()が終わる。
channel
goroutine間におけるデータを受け渡しするパイプ
送信:channel<-value
受信:<-channel
func task1(ch chan string) {
time.Sleep(time.Second)
// 値をチャネルへ送信
ch <- "task1 finished..."
}
func task2() {
fmt.Println("task2 finished...")
}
func main() {
// チャンネルを宣言
ch := make(chan string)
go task1(ch)
go task2()
// チャネルから受信
fmt.Println(<-ch)
// 受信するまで処理が止まっているのでtime.Sleep()は不要
}
task1()、task2()が走り、すぐにtask2()は終わる。
1秒後にtask1()の処理が進み、データをチャネルへ送信。
main関数の中でチャネルからデータを受信し、全体の処理が終了。
defer
defer へ渡した関数の実行を、呼び出し元の関数の終わり(returnする)まで遅延させる。
つまり、その関数が終了する際に実行したい処理を記述することができる。
複数の関数を渡した場合はLIFOの順番で実行される。
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
hello
world