A Tour of Goやってみた
Packages
- phpのようにルートからパッケージまでの経路を記述するのではなく、親パッケージの名前を書くだけで良い
- 親パッケージの名前が重複した場合どうなるのか?
Imports
- a tour of goはfactored import statementを推奨している
Exported names
- javascriptみたいなイメージを持った
- 関数のexportの面で似ていると感じた
// javascript export function add(a, b) { return a + b; } // golang func Add(a int, b int) int { return a + b }
Functions
- Goの関数定義の方法が書かれていた
- この記法である理由:https://go.dev/blog/declaration-syntax
Functions continued
- 引数に同じ型を取る場合は、型名を省略することができる
add(a int, b int)
→add(a, b int)
Multiple results
- 関数は複数の戻り値を返すことができます
Named return values
- 戻り値となる変数に名前をつけることができる
- return ステートメントに何も書かなくて良い
- 長いメソッドで使用すると可読性が下がる
- 短いの記述とは?
Variables with initializers
- varで変数を宣言することができる
:=
との違いは何?- 関数外で使用するようなグローバルな変数に対してはvarを使用する、関数内で宣言する変数に関してはShort variable declarationsを使用する
Short variable declarations
:=
で暗黙的な型宣言ができる- 関数外では使用できない
- 定数は宣言できない
Basic types
bool string int int8 int16 int32 int64 uint uint8 uint16 uint32 uint64 uintptr byte // uint8 の別名 rune // int32 の別名 // Unicode のコードポイントを表す float32 float64 complex64 complex128
Zero values
- varで宣言だけして値を代入していない変数
- 数値型(int,floatなど): 0
- bool:false
- string:""
Constants
const
で定義する
構文
For
- 他の言語と変わらない
- Goにはwhileは存在しないため、for文で実現する
- 何も記述しないと無限ループを実現することができる
// while count := 1 for count < 5 { fmt.Println(count) count++ } // 無限ループ for {}
if
- 他の言語と同じ
- 省略は以下のようになる(ifステートメントで宣言された変数はブロック内でのみ使用可能)
// 省略 if v := math.Pow(x, n); v < lim { return v }
Exercise: Loops and Functions ???
Switch
if-else
の簡略化- caseの最後に必要なbreak文が自動で提供されているため、breakは不要
- 条件なしで記述することができる(
if-then-else
の簡略化)
t := time.Now() switch { case t.Hour() < 12: fmt.Println("Good morning!") case t.Hour() < 17: fmt.Println("Good afternoon.") default: fmt.Println("Good evening.") } if t.Hour() < 12 { fmt.Println("Good morning!") } else if t.Hour() < 17 { fmt.Println("Good afternoon.") } else { fmt.Println("Good evening.") }
Defer
- deferに渡した関数を呼び出し元の関数がreturnされるまで遅延させる
- 途中で抜けようが抜けまいが必ずやって欲しい処理をあらかじめ書ける(https://qiita.com/ika_tarou/items/f9d61b7a352d946ed20c)
- deferへ渡した関数が複数ある場合はLIFOの順でスタックされる(最後に入れたものが最初に取り出される)
新しい型の定義
Pointers
- ポインタは値のメモリアドレスを指します
&
でポインタを参照する*
ポインタから値を取り出す
a := 42 b := &a i := 42 p = &i fmt.Println(*p) // ポインタpを通してiから値を読みだす *p = 21 // ポインタpを通してiへ値を代入する
Structs
- フィールドの集まり
- ドットでアクセスできる
- structのポインタに関しては
*
は不要
func main() { v := Vertex{1, 2} p := &v p.X = 1e9 fmt.Println(v) }
Arrays
- 配列のサイズも宣言する必要ある
- 固定長配列
- 値渡し
- スライスより使用度は低い?
arrayA := [4]int{1, 2, 3, 4} fmt.Println(arrayA) arrayB := arrayA arrayB[1] = 3 fmt.Println(arrayB) // [1 2 3 4] // [1 3 3 4]
Slices
- 配列型の上に構築された抽象化
- 可変長配列
- go言語組み込みのmake関数でもスライスを作成することができる
- 配列の参照のため、値を変更すると参照元も変わる
- スライスは配列へのポインタと要素数(length)とメモリ領域(capacity)をもつ
- 容量オーバーしたら基本的に2倍の値を確保します
- ゼロ値はnil
- appendで要素を追加することができる
- rangeで
- 反復処理を行うことができる
// 配列 names := [2]string {"yamada", "sato"} // スライス names := []string {"yamada", "sato"} // スライスの操作 name := []int{0, 1, 2, 3, 4, 5} name = name[1:4] // {1, 2, 3} len = 3 cap = name = name[:2] // {1, 2} len = 2 name = name[1:] // {2} len = 1 a := make([]int, 5, 5) // {0, 0, 0, 0, 0} // 要素の追加 a2 := append(a, 1, 2, 3, 4) // {0, 0, 0, 0, 0, 1, 2, 3, 4}
参考
capの増減のイメージが掴めない
Maps
- 連想配列のようなもの
make(map[キーの型]値の型)
- deleteで要素を削除可能
a := make(map[string]int) a["Answer"] = 42 a, exists := a["Answer"] // キーが存在したらexistsはtrue delete(a, "Answer") // 要素の削除
Method And Interface
Methods
- クラスは存在しないが型にメソッドを定義することができる
型にメソッドを定義するときは同じパッケージにある必要がある
typeとstruct違い
Pointer receivers
- 構造体の変数の値を変更したいときは、ポインタで定義する
- メソッドがレシーバが指す先の変数を変更する
- 変数のコピーは非効率
- 一般的には、値レシーバ、または、ポインタレシーバのどちらかですべてのメソッドを与え、混在させるべきではありません。
type Squere struct { Height int Width int } func (s *Squere) Scale(f int) { s.Height = s.Height * f s.Width = s.Width * f } s := Squere{3, 4} s.Scale(10) // height: 30, width: 40
Interfaces
- インターフェースを実装することを明示的に宣言する必要はないのでimplementsキーワードなどは不要
- Go では nil をレシーバーとして呼び出されても適切に処理するメソッドを記述するのが一般的です
- 空のインターフェースは、未知の型の値を扱うコードで使用されます(empty interface)PHPでいうmixed、JavaScriptでいうany
t, ok := i.(T)
で特定の型を保持しているかテストできる
type I interface { M() } type T struct { S string } func main() { var i I var t *T i = t } // 型の保持の確認 var t interface{} = "hello" s, ok := t.(string) // "hello", true f, ok := t.(float64) // panic
Errors
- GoではエラーをErrorオブジェクトで表現する。その多言語では例外処理でExceptionなどが存在する
- error値がnilかどうかでハンドリングを行う
func run() error { return &MyError{ time.Now(), "it didn't work", } } func main() { if err := run(); err != nil { fmt.Println(err) } }