260 Views
December 14, 25
スライド概要
2025年12月13日(土)に兵庫県神戸市で開催された「Go Workshop Conference 2025 IN KOBE」にて招待ワークショップとして「はじめてのGo言語教室」を開催した際に用意したテキスト「Go入門ハンズオン [基礎編]」です。イベントでは1時間30分向けの「Go入門ハンズオン [基礎編](短縮版)」(https://www.docswell.com/s/qt-luigi/5X6X7X-the-first-go-room-basic-short-20251213)を使用しています。
主にICTイベントでの発表資料を公開しています。
Go入門ハンズオン [基礎編] はじめてのGo言語教室 13 December 2025 Ryuji Iwata Okayama, Japan
Ryuji Iwata X (Twitter) : @qt_luigi Portfolio : sites.google.com/view/ryuji-iwata-portfolio 2
[補足] お品書き • 何を作るの? • 作業ディレクトリーの準備 • Hello "Go" World • ソースファイルの解説 • 配列とスライス • 反復 • 分岐 • 標準入出力 • 完成に向けて • まとめ 3
何を作るの? 4
成果物 タイピングゲームを作ろう! q@w@p $ ./typing The input : the x first input : first ○ Go input : go x room input : room ○ 5
基本仕様 表示する単語を配列で用意します。 [ "The", "first", "Go", "room" ] 以下、配列の要素の数だけ、添字(インデックス)の順番に繰り返します。 1.単語を一つ表示します。 The 2.単語を入力します。 input : The 3.表示した単語と入力した単語が一致なら○を、不一致なら×を表示します。 ○ 6
作業ディレクトリーの準備 7
作業ディレクトリーの作成 1.cdコマンドでホームディレクトリーに移動。 $ cd (Windows: cd %HOMEPATH%) 2.mkdirコマンドで作業ディレクトリーを作成(ここではtyping) $ mkdir typing 3.cdコマンドで作業ディレクトリーに移動。 $ cd typing 8
作業ディレクトリーの初期化 実行:go mod init モジュールパス • Goのプロジェクトとして初期化(ここではexample.com/typing) $ go mod init example.com/typing [補足] GitHub上で管理する場合の例 $ go mod init github.com/アカウント名/typing ls (dir) コマンドでgo.modファイルが作成されていることを確認。 $ ls (Windows: dir) go.mod 9
[補足] modファイル群 go.modファイルの中身 module example.com/typing go 1.25.5 外部パッケージを使うようになるとgo.sumファイルも作成される。 これらは、使用する外部パッケージやそのバージョンの管理などで使用される。 10
Hello "Go" World 11
ソースファイルの作成 テキストエディターにて、以下の内容でファイル名を「main.go」にして保存。 package main import "fmt" var ( msg1="Hello" msg2 string ) func main() { msg2="world" fmt.Println(msg1, msg2) } ※ このまま書き写してください。 12
ソースファイルの整形 ls (dir) コマンドでmain.goファイルが作成されていることを確認。 $ ls (Windows: dir) go.mod main.go 実行:go fmt ソースファイル名 • 整形されたソースファイルがあれば、そのファイル名が表示される。 $ go fmt main.go main.go [補足] 整形されたソースファイルがなければ、何も表示されない。 $ go fmt main.go (何も表示されない) [補足] テキストエディターの設定によっては、保存時に実行させるかも。 13
実行結果 package main import "fmt" var ( msg1 = "Hello" msg2 string ) func main() { msg2 = "world" fmt.Println(msg1, msg2) } [補足] ソースファイルを整形(フォーマット)することで • コードが同じ書式になるため、誰が書いても誰が見ても一定を保てる。 • ソースファイルの差分を見たとき、意図した変更点だけを確認できる。 14
ソースファイルの実行 実行:go run ソースファイル名 $ go run main.go Hello world 15
ソースファイルの解説 16
main.goファイル // 実行できるプログラムを書く場合、パッケージ名はmainを指定 package main // 使用するパッケージを指定 import "fmt" // パッケージ内で使用する変数を宣言 var ( msg1 = "Hello" // ①宣言+初期値を代入 msg2 string // ②宣言のみ(値は後から代入) ) // main()関数 // 実行できるプログラムを書く場合、最初に呼び出される関数(エントリーポイント) func main() { msg2 = "world" // 値を代入 fmt.Println(msg1, msg2) // 引数値を半角空白文字で区切って標準出力 } 17
ソースファイルについて ファイル拡張子は「.go」 BOMなしのUTF-8(文字コードの一種)で記述。 インデント(字下げ)はタブ文字。 18
パッケージ 定数や関数などを、意味のある単位でまとめたもの。 基本、パッケージ名は1単語で命名。 Goでは、標準パッケージの充実度がウリのひとつ。 • 圧縮、データベース、ネットワーク、暗号化、画像処理、など archive crypto fmt log reflect sync weak bufio database go maps regexp syscall builtin debug hash math runtime testing bytes embed html mine slices text cmp encoding image net sort time compress errors index os strconv unicode container expvar io path strings unique context flag iter plugin structs unsafe pkg.go.dev/std 19
変数の宣言 Goでは、変数を宣言するとゼロ値で初期化される。 var name string // 文字型: "" var age int // 数値型: 0 (他: float型) var ( tf bool // 論理型: false chn chan // 参照型: nil (他: struct型、interface型) ) [補足] 想定していた初期値と異なる値だったことで発生するミスを防げる。 20
関数 意味のある処理や利用頻度の高い処理を、ひとまとめにしたもの。 他のパッケージから呼び出せる関数は、名前の頭文字が大文字。 // fmtパッケージのPrintln()関数の呼び出し fmt.Println(msg1, msg2) [補足] Goでは、関数は多値(複数)の戻り値を返せる。 func MultiToStr(x, y int) (int, string) { m := x * y // 乗算 s := strconv.Itoa(m) // 乗算した結果を文字列に変換 return m, s // 乗算した結果とその文字列(=多値)を返す } [補足] ※今回は戻り値を持たないmain()関数のみを使用します。 • main()関数は、名前の頭文字が小文字なので他のパッケージから呼び出せない。 21
配列とスライス 22
配列 同じ型の複数の値を要素として扱う型。 最初に要素数を決めて変数を宣言するため、後から要素数を変更できない。 // 変数arrayを要素数4のstring型の配列で宣言 var array [4]string fmt.Println("初期値は", array) // 要素数はlen()組み込み関数で取得 fmt.Println("要素数は", len(array)) 実行結果 初期値は [ ] // イメージ: ["", "", "", ""] 要素数は 4 [補足] メモリ領域を固定するため、処理はスライスより速い。 23
スライス 配列にて、後から要素数を変更できる仕組み。 // 変数exampleをstring型のスライスで宣言 var example []string fmt.Println("初期値は", example) // 要素数はlen()組み込み関数で取得 fmt.Println("要素数は", len(example)) 実行結果 初期値は [] 要素数は 0 [補足] メモリ領域を動的に変更するため、処理は配列より速い。 24
[補足] スライス:内部構造 実体は、配列への参照(ポインター)を持つ構造体。 要素数が変更されると、要素数に沿った配列を再作成して参照を付け替えている。 Slice internals A slice is a descriptor of an array segment. It consists of a pointer to the array, the length of the segment, and its capacity (the maximum length of the segment). ptr *Elem len int cap int Our variable s, created earlier by make([]byte,5), is structured like this: []byte 5 5 [5]byte byte The length is the number of elements referred to by the slice. The capacity is the number of elements in the underlying array (beginning at the element referred to by the slice pointer). The distinction between length and capacity will be made clear as we walk through the next few examples. As we slice s, observe the changes in the slice data structure and their relation to the underlying array: go.dev/blog/slices-intro 25
[補足] スライス:値の追加 スライスに値を追加するにはappend()組み込み関数を使う。 var example = []string{"The", "first"} fmt.Println("追加前は", example) example = append(example, "Go", "room") fmt.Println("追加後は", example) 実行結果 追加前は [The first] // イメージ: ["The", "first"] 追加後は [The first Go room] // イメージ: ["The", "first", "Go", "room"] 通常、append()組み込み関数の戻り値と第1引数には、同じスライス名を指定する。 26
[補足] スライス:append()組み込み関数 追加される要素数が、 • 今の配列の後方の空き要素に収まる場合 1.今の配列の後方の空き要素に、指定された要素の値が設定される。 2.今の配列の参照(ポインター)を戻り値として返す。 • 今の配列の後方の空き要素を超える場合 1.容量を増やした新しい配列が生成される。 2.新しい配列に、今の配列の要素の値と指定された要素の値が設定される。 3.新しい配列の参照(ポインター)を戻り値として返す。 []byte 2 3 [5]byte byte go.dev/blog/slices-intro 27
[補足] 組み込み関数 あらかじめGoで用意されている関数。 内部・外部問わず、どのパッケージからでも呼び出せる。 関数名のみで頭文字は小文字。 Functions: append cap clear close complex copy delete imag len make max min new panic print println real recover go.dev/ref/spec#Predeclared_identifiers [補足] 非推奨の組み込み関数:print()とprintln() • 文字列は標準エラーに出力されていて、当初から廃止の予定が謳われている。 • 使用するなら、内部プリントデバッグ時や書き捨てコード内ぐらいまでかと。 28
変数宣言+初期化 変数宣言 • パッケージレベルで初期化したり、明示的にゼロ値で初期化する際に使用。 import "fmt" // パッケージレベルで初期化 var example = []string{"The", "first", "Go", "room"} func main() { ... } 省略宣言 • varを省略できるが、関数内でのみ使用可能。 example := [4]string{"The", "first", "Go", "room"} 29
要素の値の取得 単一の要素の値 example := [4]string{"The", "first", "Go", "room"} // [添字] 単一の値を取得 fmt.Println(example[2]) // 3番目の "Go" [補足] 連続する要素の値 // [開始添字:終了添字+1] 連続した値を取得 fmt.Println(example[1:3]) // 2~3番目の ["first", "Go"] // [開始添字:] それ以降の連続した値を取得 fmt.Println(example[2:]) // 3番目以降の ["Go", "room"] // [:終了添字+1] それ以前の連続した値を取得 fmt.Println(example[:2]) // 2番目以前の ["The", "first"] 30
要素の値の変更 変更したい要素の位置を添字で指定して値を代入。 example := [4]string{"The", "first", "Go", "room"} // 4番目の "room" を "world" に変更 example[3] = "world" // 4番目…添字は3 31
課題1 以下の内容でmain.goを編集した後、go runコマンドを実行して確認してください。 1-1:var句を削除した後、新たに次の変数をvar宣言してください。 • 変数名:example • 型:string型のスライス • 値:"The", "first", "Go", "room" 1-2:main()関数の内容を書き換えてください。 • 変数exampleの内容を標準出力する。 32
解答1 package main import "fmt" var example = []string{"The", "first", "Go", "room"} func main() { fmt.Println(example) } 実行結果 $ go run main.go [The first Go room] 33
反復 34
for文
ループ変数(ここではi)を使用して、ループ(処理の繰り返し)を制御する。
• [補足] 通常の書き方(変数iはfor文内でのみアクセスできる)
abc := []string{"a", "b", "c"}
for i := 0; i < 3; i++ {
fmt.Print(abc[i])
}
• [補足] 別の書き方(変数iはfor文外でもアクセスできる)
abc := []string{"a", "b", "c"}
i := 0
for i < 3 {
fmt.Print(abc[i])
i++
}
実行結果
abc
35
[補足] 書式の説明 初期化式:ループの最初に1度だけ実行。ループ変数に初期値を設定。 for i := 0; 条件式; 後処理 { ... } 条件式:ループ毎に実行。ループ変数の値を判定して満たしていればループを継続。 for 初期化式; i < 3; 後処理 { ... } 後処理:ループ内で実行。ループ変数の値を変更。 for 初期化式; 条件式; i++ { ... } 36
[補足] 条件式がなければ無限ループ 条件式(ループ変数の値の判定)をなくすと、無限ループになる。 i := 1 for { fmt.Print(i) i++ } 実行結果 12345678910111213141516171819202122232425262728293031323334363738394041424344454647484950515253... ※ [ctrl] (macOS: [command]) + [C] キーで停止 37
range句 複数の値を保持する変数において、値の数だけ繰り返す。 ループ毎に1つまたは2つの戻り値が取得できる。 • 配列とスライス:添字と値 • マップ:キーと値(例:{"red":3, "blue":4, ...} • 文字列:添字(バイトインデックス)と値(Unicodeコードポイント) • チャネル:値(要素) ※今回は配列とスライスを使用します。 38
配列とスライス:添字と値を取得 第1戻り値変数(i)と第2戻り値変数(v)を指定する。 abc := []string{"a", "b", "c"} for i, v := range abc { fmt.Println(i, v) } 実行結果 0 a 1 b 2 c 39
配列とスライス:添字だけを取得 第1戻り値変数(i)のみ指定する。 abc := []string{"a", "b", "c"} for i := range abc { fmt.Println(i) } 実行結果 0 1 2 40
配列とスライス:値だけを取得 第1戻り値変数(_)と第2戻り値変数(v)を指定する。 abc := []string{"a", "b", "c"} for _, v := range abc { fmt.Println(v) } 実行結果 a b c 41
_:ブランク識別子 Goでは、受け取るべき場所に変数がないと、エラーになる。 そのため、ダミー変数として「_」が用意されている。 [補足] ブランク演算子を用いることで • 未使用の変数宣言によるメモリ消費やGCの無用な追跡を防ぐ。 • 多値の戻り値を受け取る時の変数の数の曖昧さを排除。 • 構造体の変数定義で、連続したメモリ領域を明示的に確保。 ※ GC (Garbage Collection):プログラムが使用するメモリ領域を調節する機能 42
課題2 以下の内容でmain.goを編集した後、go runコマンドを実行して確認してください。 2-1:main()関数の内容を書き換えてください。 • for文とrange句を使用して、変数exampleの値を順番に標準出力する。 43
解答2 package main import "fmt" var example = []string{"The", "first", "Go", "room"} func main() { for _, v := range example { fmt.Println(v) } } 実行結果 $ go run main.go The first Go room 44
[補足] 解答2
range句を使わなかった場合
package main
import "fmt"
var example = []string{"The", "first", "Go", "room"}
func main() {
for i := 0; i < len(example); i++ {
v := example[i]
fmt.Println(v)
}
}
45
分岐 46
if文 Goでは、条件(三項)演算子(例:lang == "Go" ? 1 : 0)はなし。 [補足] 他にswitch文があるが、今回は省略。 v := "GoLang" if v == "golang" { fmt.Println("○") } else if v == "GOLANG" { fmt.Println("△") } else { fmt.Println("×") } fmt.Println(v) 実行結果 × GoLang 47
[補足] 変数をifブロックに閉じ込める
条件式の前に初期化式を記述できる。
初期化式で宣言した変数は、そのif文内だけで使用できる。
if v := "GoLang"; v == "golang" {
fmt.Println("○")
} else if v == "GOLANG" {
fmt.Println("△")
} else {
fmt.Println("×")
}
// 変数vのネームスコープはif文内だけになるため、外からアクセスするとエラー
fmt.Println(v)
実行結果
# command-line-arguments
./main.go:17:17: undefined: v
48
課題3 以下の内容でmain.goを編集した後、go runコマンドを実行して確認してください。 3-1:main()関数のfor文のfmt.Println(v)の後に、次の処理を追加してください。 1.ループ変数vの値を判定するif文を追加してください。 • 値が"Go"なら"○"を標準出力する。 • それ以外なら"×"を標準出力する。 49
解答3 package main import "fmt" var example = []string{"The", "first", "Go", "room"} func main() { for _, v := range example { fmt.Println(v) if v == "Go" { fmt.Println("○") } else { fmt.Println("×") } } } 50
解答3 実行結果 $ go run main.go The × first × Go ○ room × 51
標準入出力 52
標準入出力 標準入力 • 情報の入力元:キーボード、ファイル、など 標準出力 • 情報の出力先:ターミナル、ファイル、など [補足] 標準エラー出力 • エラー情報の出力先:ターミナル、ファイル、など • ※見た目は標準出力と同じであることが多い。 53
標準入力された文字列の取得 Goでは、キーボードから文字を入力して改行された時までの文字列を取得するための • fmt.Scanln()関数 が用意されており、標準入力された値は関数内で引数(ここではa)に格納される。 func Scanln(a ...any) (n int, err error) 54
これまでの書き方:値渡し Goでは、関数の引数は関数内にコピーされて渡される。 fmt.Scanln()関数の中では、コピーされた引数に入力値が格納されている。 • コピーされた引数と値の変更は、関数内だけで有効。 関数が終了した後、引数の値を確信すると最初のまま。 // 入力プロンプトを標準出力に表示 fmt.Print("入力は?> ") var answer string fmt.Scanln(answer) fmt.Printf("入力は %s です\n", answer) 実行結果 入力は?> 入力は です 55
今回の書き方:参照渡し Goでは、変数名の先頭に&をつけると、値ではなく • 値が格納されている場所(参照:ポインター) を格納するようになる。 関数内ではコピーされた参照に値が格納されるので、引数の値が書き変わる。 // 入力プロンプトを標準出力に表示 fmt.Print("入力は?> ") var answer string fmt.Scanln(&answer) fmt.Printf("入力は %s です\n", answer) 実行結果 入力は?> Go 入力は Go です 56
値渡しと参照渡し ファイルとショートカットに例えるとわかりやすいかと。 値渡し:ファイル(値)を渡す • 関数内ではコピーされたファイルに値が書き込まれる。 • 関数に渡す前のファイルを開くと「変更されていない」 参照渡し:ファイルのショートカット(参照)を渡す • 関数内ではコピーされたショートカットのファイルに値が書き込まれる。 • 関数に渡す前のファイルを開くと「変更されている」 57
fmtパッケージ 標準出力(Print系)、標準入力(Scan系)、標準エラー(Error系)の関数が体系的に集 約されているため、最初に読み始めるパッケージとして良いかと。 GO fmt package standard library 0 Version: go1.23.1 Latest | Published: Sep 5, 2024 | License: BSD-3-Clause | Imports: 10 | Imported by: 4,584,904 Jump to ... f Index Documentation func Append([]byte, ...any) []byte func Appendf([]byte, format string, a ...any) []byte Overview func Appendln([]byte, ...any) []byte Index func Errorf(format string, a ...any) error Examples func FormatString(state State, verb rune) string Constants func Fprint(w io.Writer, a ...any) (n int, err error) Variables func Fprintf(w io.Writer, format string, a ...any) (n int, err error) Functions func Fprintln(w io.Writer, a ...any) (n int, err error) Types func Fscan(r io.Reader, a ...any) (n int, err error) Source Files func Fscanf(r io.Reader, format string, a ...any) (n int, err error) func Fscanln(r io.Reader, a ...any) (n int, err error) func Print(a ...any) (n int, err error) func Printf(format string, a ...any) (n int, err error) func Println(a ...any) (n int, err error) func Fprintln(os.Stderr, a ...any) (n int, err error) func Scan(a ...any) (n int, err error) func Scanf(format string, a ...any) (n int, err error) func Scanln(a ...any) (n int, err error) func Sprint(a ...any) string func Sprintf(format string, a ...any) string func Sprintln(a ...any) string func Sscan(str string, a ...any) (n int, err error) func Sscanf(str string, format string, a ...any) (n int, err error) func Sscanln(str string, a ...any) (n int, err error) type Formatter type GoStringer type ScanState type Scanner type State type Stringer pkg.go.dev/fmt#pkg-index 58
[補足] Print系 // 改行させる場合は、改行コード「\n」を含める。 fmt.Print("golang ") fmt.Print("2025\n") // 最後にf付きは、フォーマット指定で出力。 fmt.Printf("%s %d\n", "golang", 2025) // 最後にln付きは、文末に改行コードが付く。 fmt.Println("golang", "2025") // 最初にF付きは、第1引数に出力先を指定(ここでは標準エラー) fmt.Fprintln(os.Stderr, "golang", "2025") // 最初にS付きは、文字列を返す fmt.Println(fmt.Sprintf("%s %d", "golang", 2025)) 実行結果 golang 2025 59
課題4 以下の内容でmain.goを編集した後、go runコマンドを実行して確認してください。 4-1:main()関数のfor文のfmt.Println(v)とif文の間に、次の処理を追加してください。 1.入力プロンプトとしての"input:"を改行なしで標準出力してください。 2.次の変数をvar宣言してください。 • 変数名:answer • 型:string型 3.キーボードから入力して改行された文字列を、変数answerに格納してください。 4.ループ変数vの値を判定するif文の条件式を書き換えてください。 • vの値がanswerの値と一致なら"○"を標準出力する。 60
解答4 package main import "fmt" var example = []string{"The", "first", "Go", "room"} func main() { for _, v := range example { fmt.Println(v) fmt.Print("input : ") var answer string fmt.Scanln(&answer) if v == answer { fmt.Println("○") } else { fmt.Println("×") } } } 61
解答4 実行結果 $ go run main.go The input : The ○ first input : first ○ Go input : Go ○ room input : loom × 62
完成に向けて 63
実行ファイルを生成 ls (dir) コマンドでディレクトリー内のファイルの存在を確認。 $ ls (Windows: dir) go.mod main.go 実行:go build ソースファイル名 $ go build main.go $ ls (Windows: dir) go.mod main main.go • ソースファイルと同名の実行ファイル「main」が生成される。 実行:go build -o 実行ファイル名 ソースファイル名 $ go build -o typing main.go (Windows: go build -o typing.exe main.go) $ ls (Windows: dir) go.mod main main.go typing (Windows: typing.exe) • 指定した名前の実行ファイル「typing(.exe)」が生成される。 64
完成! q@w@p 生成された実行ファイルを実行。 $ ./typing (Windows: .\typing.exe) The input : The ○ first input : first ○ Go input : Go ○ room input : room ○ 65
まとめ 66
学んだこと Goの基本的なソースコードの構成とプログラミングの流れを学びました。 その際、goコマンドについても学びました。 • go mod init • go fmt • go run • go build データ構造における配列とスライスについて簡単に学びました。 構造化プログラミングにおける「順接・反復・分岐」の制御構成を学びました。 標準出力と標準入力について簡単に学びました。 67
続きは、公式サイトのコンテンツで 公式のチュートリアルを進めるのが良いかと。 • Tutorials - The Go Programming Language go.dev/doc/tutorial/ 公式の文法習得ツアーに取り組むも良し。 • A Tour of Go go.dev/tour/list いつでも気軽にGoをコーディング&実行。 • Go Playground - The Go Programming Language go.dev/play/ 68
Go Playground - Goの遊び場
go.dev/play/
=GO Why Go Learn Docs Packages Community
The Go Playground Go 1.25 Run Format Share Hello, World!
Press Esc to move out of the editor.
1 // You can edit this code!
2 // Click here and start typing.
3 package main
4
5 import "fmt"
6
7 func main() {
8 fmt.Println("Hello, 世界")
9 }
10
Hello, 世界
Program exited.
About the Playground
The Go Playground is a web service that runs on go.dev's servers. The service receives a Go program, vets, compiles, links, and runs the program
inside a sandbox, then returns the output.
If the program contains tests or examples and no main function, the service runs the tests. Benchmarks will likely not be supported since the
program runs in a sandboxed environment with limited resources.
69
Thank you Ryuji Iwata Okayama, Japan @qt_luigi