Closed18

A Tour of Go やってみた Part1

kun432kun432

ゆえあってGoに入門したい。まずは「A Tour of Go」から。結構ボリュームはある・・・

https://go-tour-jp.appspot.com/list

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は、どうやら英語版と日本語版で書いてあることが違うらしい。

https://qiita.com/okaponta_/items/50ab02605a5f3b6bf5bd

インストール

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のインストール以外は不要というオチ。

kun432kun432

Basics: Packages, variables, and functions

1. Packages

すべての Go プログラム はパッケージで構成され、プログラムはパッケージ main で実行開始される。

ここでは、fmtmath/randパッケージをインポートしている。

パッケージ名は、インポートで指定するパスの最後と同じになる。
(例: math/randの場合はpackage rand

1.go
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
go.mod
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で実行することとする。

kun432kun432

2. Imports

importは以下のようにグループ化してインポート(_factored import statement_というらしい)することもできれば、

2.go
package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Printf("あなたは今 %g 個の問題を抱えています。\n", math.Sqrt(7))
}
出力
今あなたは 2.6457513110645907 の課題を抱えています。

個別のimportステートメントで書くこともできる。ただし、前者のほうが推奨。

2.go
package main

import "fmt"
import "math"

func main() {
	fmt.Printf("今あなたは %g の課題を抱えています。\n", math.Sqrt(7))
}
出力
今あなたは 2.6457513110645907 の課題を抱えています。
kun432kun432

3. Exported names

最初の1文字目が大文字の名前はエクスポートされた名前になり、外部のパッケージからインポートして参照できる。小文字はエクスポートされていない名前となり、外部からアクセスできない。

以下のPimathパッケージで公開されている。

3.go
package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println(math.Pi)
}
出力
3.141592653589793

小文字にするとエラーになる

3-2.go
package main

import (
	"fmt"
	"math"
)

func main() {
	fmt.Println(math.pi)
}
出力
# command-line-arguments
./3.go:9:19: undefined: math.pi
kun432kun432

4. Functions

関数。0個以上の引数を取ることが可能。

4.go
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型は存在しないらしいので、単に戻り値の方を書かなければいいだけみたい。

4-2.go
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
kun432kun432

5. Function continued

関数の引数の型が同じ場合は最後の引数の型にまとめて書ける。

5.go
package main

import "fmt"

func add(x, y int) int {
	return x + y
}

func main() {
	fmt.Println(add(42, 13))
}
出力
55
kun432kun432

6. Multiple results

関数は複数の戻り値を返すことができる。

6.go
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)
}
kun432kun432

7. Named return values

戻り値の変数に名前をつけておくと、関数内でその変数を使うことができ、関数のドキュメント的に使うことができる。また、return 時に何も指定しなくても定義した戻り値でreturnされる(_naked_returnと呼ぶ)。短い関数であればよいが、長い関数の場合は、おそらく何が返るかを関数宣言の先頭まで確認しない時計ないので、読みにくくなる、ってことなのかな?

7.go
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
kun432kun432

8. Variables

var 変数名 型で変数を宣言する。複数の変数の場合は最後の変数の後に型を書く。パッケージ・関数の両方で使える。

8.go
package main

import "fmt"

var c, pythom, java bool

func main() {
	var i int
	fmt.Println(i, c, pythom, java)
}
出力
0 false false false
kun432kun432

9. Variables with initializers

変数に初期化子(initializers)を与えて、初期値を代入できる。この場合は、型を省略でき、初期化子の型が設定される。

9.go
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!
kun432kun432

10. Short variable declarations

関数内ではvarの代わりに:=が使って暗黙の型宣言が可能。ただし関数外ではキーワード(varfunc)などのキーワードで宣言する必要があり、:=のような暗黙的宣言はできない。

10.go
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!
kun432kun432

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 エンコーディング) "" (空文字列) 長さは理論上メモリ/アドレス空間に依存(制限なし)
  • byteuint8 の別名、runeint32 の別名です。
  • intuint はプラットフォーム依存ですが、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)
kun432kun432

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 ""
kun432kun432

13. Type conversions

変数v、型TT(v)することで型変換できる。

13.go
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

もう一つ

13-2.go
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で型変換する場合には明示的に行う必要がある。型変換している部分をなくしてみる。

13-3.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
kun432kun432

14. Type inference

明示的に型を指定せずに変数を宣言した場合、その変数の型は右側の変数から型推論される。

14.go
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になる

14-2.go
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)
kun432kun432

15. Constants

定数はconstで変数と同様に宣言する。文字・文字列・boolean・numericのみ指定でき、:=では宣言できない。

15.go
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
kun432kun432

16. Numeric Constants

数値の定数は高精度な値であり、型がない定数は状況によって必要な型を取る。

16.go
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
このスクラップは3ヶ月前にクローズされました