A Tour of Go やってみた Part1
ゆえあってGoに入門したい。まずは「A Tour of Go」から。結構ボリュームはある・・・
Webでできるんだけど、ローカルでもできるみたい。Macの場合はHomebrewでインストールできる。
brew install go
go version
go version go1.24.4 darwin/arm64
A Tour of Goはローカルでも実行できるらしいのだが・・・
go tool tour
そんなツールはないと言われる・・・
go: no such tool "tour"
A Tour of Goは、どうやら英語版と日本語版で書いてあることが違うらしい。
インストール
go install golang.org/x/website/tour@latest
go: downloading golang.org/x/website v0.0.0-20250612141559-74958936af9b
go: downloading golang.org/x/tools v0.34.0
go: downloading golang.org/x/net v0.41.0
go: downloading golang.org/x/mod v0.25.0
go: downloading github.com/yuin/goldmark v1.6.0
go: downloading golang.org/x/sync v0.15.0
GOPATHに移動して実行すれば良い
cd $(go env GOPATH)/bin
./tour
日本語版もあるようなのだが、上手くインストールできなかった。まあ、Webでやるほうがお手軽かも。
以後はWebの日本語版を見つつ、手に慣れさせるためにもローカルでCursor等を使いながら進めようと思う。
goのインストール以外は不要というオチ。
Basics: Packages, variables, and functions
1. Packages
すべての Go プログラム はパッケージで構成され、プログラムはパッケージ main
で実行開始される。
ここでは、fmt
、math/rand
パッケージをインポートしている。
パッケージ名は、インポートで指定するパスの最後と同じになる。
(例: math/rand
の場合はpackage rand
)
package main
import (
"fmt"
"math/rand"
)
func main() {
fmt.Println("私の好きな数字は", rand.Intn(10))
}
実行。go run
でその場で実行。
go run 1.go
go run 1.go
実行方法は他にもある。go build
でコンパイルして実行。
go build 1.go
ls -lt
実行ファイルが生成されている
total 4608
-rwxr-xr-x@ 1 kun432 staff 2353490 6 16 23:20 1
-rw-r--r--@ 1 kun432 staff 117 6 16 23:18 1.go
file 1
1: Mach-O 64-bit executable arm64
実行ファイルを実行
./1
私の好きな数字は 6
モジュールを使用するパターン。一旦ビルドされたバイナリを削除。
rm 1
go.modを作成。ここではモジュール名を"sample"とした。
go mod init sample
module sample
go 1.24.4
go run
で実行すると必要なファイルを読み込んでくれるみたい。
go run .
私の好きな数字は 6
モジュールからビルド(という表現でいいんだろうか?)
go build .
ls -lt
指定したモジュール名から実行ファイルが作成される。
total 4616
-rwxr-xr-x@ 1 kun432 staff 2353490 6 16 23:26 sample
-rw-r--r--@ 1 kun432 staff 25 6 16 23:24 go.mod
-rw-r--r--@ 1 kun432 staff 117 6 16 23:18 1.go
実行
./sample
私の好きな数字は 9
なるほど。とりあえずgo.mod
と作成された実行ファイルは削除しておく。
rm go.mod sample
以後はgo run
で実行することとする。
2. Imports
importは以下のようにグループ化してインポート(_factored import statement_というらしい)することもできれば、
package main
import (
"fmt"
"math"
)
func main() {
fmt.Printf("あなたは今 %g 個の問題を抱えています。\n", math.Sqrt(7))
}
今あなたは 2.6457513110645907 の課題を抱えています。
個別のimportステートメントで書くこともできる。ただし、前者のほうが推奨。
package main
import "fmt"
import "math"
func main() {
fmt.Printf("今あなたは %g の課題を抱えています。\n", math.Sqrt(7))
}
今あなたは 2.6457513110645907 の課題を抱えています。
3. Exported names
最初の1文字目が大文字の名前はエクスポートされた名前になり、外部のパッケージからインポートして参照できる。小文字はエクスポートされていない名前となり、外部からアクセスできない。
以下のPi
はmath
パッケージで公開されている。
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.Pi)
}
3.141592653589793
小文字にするとエラーになる
package main
import (
"fmt"
"math"
)
func main() {
fmt.Println(math.pi)
}
# command-line-arguments
./3.go:9:19: undefined: math.pi
4. Functions
関数。0個以上の引数を取ることが可能。
package main
import "fmt"
func add(x int, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
55
引数は変数名 型
で指定、複数の場合はカンマで区切る。そして戻り値の方を宣言する。
func 関数名(変数名 型[, 変数名 型, ...]) 戻り値の型 {
...関数内の処理を書く...
}
なお、戻り値を返さないvoidの場合、void型は存在しないらしいので、単に戻り値の方を書かなければいいだけみたい。
package main
import "fmt"
func add(x, y int) {
fmt.Println(x + y)
}
func print() {
fmt.Println("Print!")
}
func main() {
print()
add(42, 13)
}
Print!
55
5. Function continued
関数の引数の型が同じ場合は最後の引数の型にまとめて書ける。
package main
import "fmt"
func add(x, y int) int {
return x + y
}
func main() {
fmt.Println(add(42, 13))
}
55
6. Multiple results
関数は複数の戻り値を返すことができる。
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("こんにちは!", "世界!")
fmt.Println(a, b)
}
世界! こんにちは!
ちなみに、:=
は短縮変数宣言というらしい。変数を宣言しつつ、戻り値を代入する。通常の=
は基本的に変数が事前に宣言されている必要があるみたい。
なのでこう書くこともできる
func main() {
var a, b string
a, b = swap("こんにちは!", "世界!")
fmt.Println(a, b)
}
7. Named return values
戻り値の変数に名前をつけておくと、関数内でその変数を使うことができ、関数のドキュメント的に使うことができる。また、return
時に何も指定しなくても定義した戻り値でreturnされる(_naked_returnと呼ぶ)。短い関数であればよいが、長い関数の場合は、おそらく何が返るかを関数宣言の先頭まで確認しない時計ないので、読みにくくなる、ってことなのかな?
package main
import "fmt"
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return
}
func main() {
fmt.Println(split(17))
}
7 10
8. Variables
var 変数名 型
で変数を宣言する。複数の変数の場合は最後の変数の後に型を書く。パッケージ・関数の両方で使える。
package main
import "fmt"
var c, pythom, java bool
func main() {
var i int
fmt.Println(i, c, pythom, java)
}
0 false false false
9. Variables with initializers
変数に初期化子(initializers)を与えて、初期値を代入できる。この場合は、型を省略でき、初期化子の型が設定される。
package main
import "fmt"
var i, j string = "1,", "2"
func main() {
var c, python, java = true, false, "no!"
fmt.Println(i, j, c, python, java)
}
1, 2 true false no!
10. Short variable declarations
関数内ではvar
の代わりに:=
が使って暗黙の型宣言が可能。ただし関数外ではキーワード(var
、func
)などのキーワードで宣言する必要があり、:=
のような暗黙的宣言はできない。
package main
import "fmt"
func main() {
var i, j int = 1, 2
k := 3
c, python, java := true, false, "no!"
fmt.Println(i, j, k, c, python, java)
}
1 2 3 true false no!
11. Basic types
基本の組み込み型。o4-miniにまとめてもらった。
型 | 説明 | ゼロ値 | 取りうる範囲(レンジ) |
---|---|---|---|
bool |
真偽値 | false |
false または true
|
符号付き整数 | |||
int8 |
8bit 符号付き整数 | 0 |
–128 … +127 |
int16 |
16bit 符号付き整数 | 0 |
–32 768 … +32 767 |
int32 |
32bit 符号付き整数 | 0 |
–2 147 483 648 … +2 147 483 647 |
int64 |
64bit 符号付き整数 | 0 |
–9 223 372 036 854 775 808 … +9 223 372 036 854 775 807 |
int |
実装依存(通常 64bit) | 0 |
64bit 環境: –9.22×10¹⁸ … +9.22×10¹⁸/32bit 環境: –2.15×10⁹ … +2.15×10⁹ |
符号なし整数 | |||
uint8 (byte ) |
8bit 符号なし整数 | 0 |
0 … 255 |
uint16 |
16bit 符号なし整数 | 0 |
0 … 65 535 |
uint32 |
32bit 符号なし整数 | 0 |
0 … 4 294 967 295 |
uint64 |
64bit 符号なし整数 | 0 |
0 … 18 446 744 073 709 551 615 |
uint |
実装依存(通常 64bit) | 0 |
64bit 環境: 0 … 1.84×10¹⁹/32bit 環境: 0 … 4.29×10⁹ |
浮動小数点 | IEEE-754 準拠 | ||
float32 |
単精度(32bit) | 0.0 |
約 –3.4×10³⁸ … +3.4×10³⁸ 最小正規化数 ≈1.18×10⁻³⁸ |
float64 |
倍精度(64bit) | 0.0 |
約 –1.8×10³⁰⁸ … +1.8×10³⁰⁸ 最小正規化数 ≈2.23×10⁻³⁰⁸ |
複素数 | 実部+虚部を持つ | ||
complex64 |
実部・虚部が float32 範囲の複素数 |
0+0i |
実部・虚部ともに float32 のレンジ |
complex128 |
実部・虚部が float64 範囲の複素数 |
0+0i |
実部・虚部ともに float64 のレンジ |
文字・文字列 | |||
rune (int32 ) |
Unicode コードポイント | 0 |
–2 147 483 648 … +2 147 483 647 |
string |
不変のバイト列(UTF-8 エンコーディング) |
"" (空文字列) |
長さは理論上メモリ/アドレス空間に依存(制限なし) |
-
byte
はuint8
の別名、rune
はint32
の別名です。 -
int
/uint
はプラットフォーム依存ですが、Go の主要サポート環境は 64bit のため、64bit 想定で考えるとよいでしょう。 -
string
の長さに制限はなく、利用可能メモリとアドレス空間の範囲で任意の長さを扱えます。
package main
import (
"fmt"
"math/cmplx"
)
var (
ToBe bool = false
maxInt uint64 = 1<<64 - 1
z complex128 = cmplx.Sqrt(-5 + 12i)
)
func main() {
fmt.Printf("型: %T 値: %v\n", ToBe, ToBe)
fmt.Printf("型: %T 値: %v\n", maxInt, maxInt)
fmt.Printf("型: %T 値: %v\n", z, z)
}
型: bool 値: false
型: uint64 値: 18446744073709551615
型: complex128 値: (2+3i)
12. Zero values
変数に初期値を与えずに宣言した場合は「ゼロ値」(zeo value)が与えられる。ゼロ値は型によって異なる。
- 数値型:
0
- bool型:
false
- string型: "" (空文字)
package main
import "fmt"
func main() {
var i int
var f float64
var b bool
var s string
fmt.Printf("%v %v %v %q\n", i, f, b, s)
}
0 0 false ""
13. Type conversions
変数v
、型T
をT(v)
することで型変換できる。
package main
import (
"fmt"
)
func main() {
var i int = 42
var f float64 = float64(i)
var u uint = uint(f)
fmt.Printf("i: 型: %T 値: %v\n", i, i)
fmt.Printf("f: 型: %T 値: %v\n", f, f)
fmt.Printf("u: 型: %T 値: %v\n", u, u)
// よりシンプルな書き方
i2 := 42
f2 := float64(i)
u2 := uint(f)
fmt.Printf("i2: 型: %T 値: %v\n", i2, i2)
fmt.Printf("f2: 型: %T 値: %v\n", f2, f2)
fmt.Printf("u2: 型: %T 値: %v\n", u2, u2)
}
i: 型: int 値: 42
f: 型: float64 値: 42
u: 型: uint 値: 42
i2: 型: int 値: 42
f2: 型: float64 値: 42
u2: 型: uint 値: 42
もう一つ
package main
import (
"fmt"
"math"
)
func main() {
var x, y int = 3, 4
fmt.Println(x, y)
var f float64 = math.Sqrt(float64(x*x + y*y))
fmt.Println(f)
var z uint = uint(f)
fmt.Println(x, y, z)
}
3 4
5
3 4 5
Goで型変換する場合には明示的に行う必要がある。型変換している部分をなくしてみる。
package main
import (
"fmt"
"math"
)
func main() {
var x, y int = 3, 4
fmt.Println(x, y)
var f float64 = math.Sqrt(x*x + y*y)
fmt.Println(f)
var z uint = f
fmt.Println(x, y, z)
}
# command-line-arguments
./13-3.go:11:28: cannot use x * x + y * y (value of type int) as float64 value in argument to math.Sqrt
./13-3.go:13:15: cannot use f (variable of type float64) as uint value in variable declaration
14. Type inference
明示的に型を指定せずに変数を宣言した場合、その変数の型は右側の変数から型推論される。
package main
import "fmt"
func main() {
var i int
j := i // j は iの型になる
fmt.Printf("j: 型: %T 値: %v\n", j, j)
}
j: 型: int 値: 0
右側が方を指定しない数値の場合、その変数は右側の定数の精度に基づいて int
/ float64
/ complex128
になる
package main
import "fmt"
func main() {
i := 42
f := 3.142
g := 0.867 + 0.5i
fmt.Printf("i: 型: %T 値: %v\n", i, i)
fmt.Printf("f: 型: %T 値: %v\n", f, f)
fmt.Printf("g: 型: %T 値: %v\n", g, g)
}
i: 型: int 値: 42
f: 型: float64 値: 3.142
g: 型: complex128 値: (0.867+0.5i)
15. Constants
定数はconst
で変数と同様に宣言する。文字・文字列・boolean・numericのみ指定でき、:=
では宣言できない。
package main
import "fmt"
const Pi = 3.14
func main() {
const World = "世界"
fmt.Println("こんにちは!", World)
fmt.Println("ハッピー", Pi, "デー")
const Truth = true
fmt.Println("Go ルール?", Truth)
}
こんにちは! 世界
ハッピー 3.14 デー
Go ルール? true
16. Numeric Constants
数値の定数は高精度な値であり、型がない定数は状況によって必要な型を取る。
package main
import "fmt"
const (
// 1ビットを100桁左にシフトして、巨大な数値を作成する。
// つまり、1の後に10 個の0が続く2進数となる。
Big = 1 << 100
// 再度99桁分右にシフトすると、1<<1、つまり 2 になる
Small = Big >> 99
)
func needInt(x int) int { return x*10 + 1 }
func needFloat(x float64) float64 {
return x * 0.1
}
func main() {
fmt.Println(needInt(Small))
fmt.Println(needFloat(Small))
fmt.Println(needFloat(Big))
}
21
0.2
1.2676506002282295e+29
続き