プログラミング言語 Go を読みながらメモ。
前回の第一章は
https://qiita.com/Nabetani/items/077c6b4d3d1ce0a2c3fd
にある。
で。
#省略変数宣言
names:=expressions
型の変数宣言で、 names
に、宣言済みの変数が合っても良いというのは意外だった。
package main
import (
"fmt"
"os"
)
func main() {
hoge, err := os.Open("hoge")
fmt.Println("hoge: ", hoge, ", err:", err)
fuga, err := os.Open("fuga") // okay, fuga is new variable
fmt.Println("fuga: ", fuga, ", err:", err)
fuga, err := os.Open("fuga") // error! no new variables on left side of :=
}
まあエラー変数をたくさん作りたくないからということだと思うんだけど、ちょっと気持ち悪い。
あと。省略変数宣言は、グローバル変数の宣言には使えない。なんでそうしたんだろう。
ポインタ
flag
パッケージの選択が乱暴で面白い。
何も指定しなくても -h
と -help
で usage が出る。flag.Parse()
で出力して exit している感じ。
new 関数
構造体や配列のサイズがゼロの場合にアドレスが重複することがあるというのに驚いた。
C++ とは異なる。
package main
import (
"fmt"
)
func main() {
a := new([0]int)
b := new([0]int)
fmt.Printf("%v %v %t\n", a, b, a == b) //=> &[] &[] true
c := new([1]int)
d := new([1]int)
fmt.Printf("%v %v %t\n", c, d, c == d) //=> &[0] &[0] false
}
手元の処理系では、同一アドレスが割り当てられているらしいことがわかる。
一方 C++ では
#include <iostream>
struct empty{};
int main()
{
auto * a = new empty;
auto * b = new empty;
std::cout << a <<", " << b << ", " << (a==b) << std::endl;
//=> 0x7fc239400300, 0x7fc239400360, 0
}
という具合で、必ず別のアドレスが割り当てられる。
型宣言
p43 に
全ての型 T に対して値 x を T型に変換するような対応する変換演算 T(x) があります。
とあるので、なんでも変換できるような気がしてしまったが、もちろんそんなことはない。
コンパイルエラーにならないという意味ですらない。
package main
import "fmt"
func main() {
str := "string"
a := int(str) // cannot convert str (type string) to type int
b := [3]int(str) // cannot convert str (type string) to type [3]int
fmt.Println(a, b)
}
無茶な変換は、ちゃんとコンパイルエラーになる。
パッケージ初期化
依存関係が循環していると、ちゃんとエラーになる。
package main
import "fmt"
var a = b + c // typechecking loop involving a = b + c
var b = c + a
var c = a + b
func main() {
fmt.Println(a, b, c)
}
実際には、
./2.6.2.go:5:5: typechecking loop involving a = b + c
/Users/nabetani/gobook/2/2.6.2.go:5:5 a
/Users/nabetani/gobook/2/2.6.2.go:7:11 a + b
/Users/nabetani/gobook/2/2.6.2.go:7:5 c = a + b
/Users/nabetani/gobook/2/2.6.2.go:7:5 c
/Users/nabetani/gobook/2/2.6.2.go:6:11 c + a
/Users/nabetani/gobook/2/2.6.2.go:6:5 b = c + a
/Users/nabetani/gobook/2/2.6.2.go:6:5 b
/Users/nabetani/gobook/2/2.6.2.go:5:11 b + c
/Users/nabetani/gobook/2/2.6.2.go:5:5 a = b + c
という、ちょっと膨大なエラーが出ていて、何がどう循環しているのかわかるようになっている。親切。
スコープ
省略変数宣言 で、前述のような驚きがあったので、下記のような省略時宣言がどうなるのかちょっと気になった。
package main
import "fmt"
func main() {
x, y := 1, 2
{
x, y := "foo", "bar"
fmt.Println(x, y) //=> foo, bar
}
fmt.Println(x, y) //=> 1, 2
}
当然、上記のようになる。そうじゃないと困るよね。
あと。
Go の普通の慣習では if ブロック内でエラーを処理してリターンします
と書いてあって、ちょっと驚いた。私もその方が好きだけど、古い「関数内に return は1個だけ!」という慣習にとらわれている人は考えを改める必要があるだろう。
第三章は
https://qiita.com/Nabetani/items/2fd9c372fcd8383955a5
にある。