0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

『初めてのGo言語 第2版』読書&学習ログ(第3章)

Last updated at Posted at 2025-08-24

はじめに

第1〜2章に続き、第3章で学んだポイントを要点+検証コードでまとめます。
前回の記事はこちら👇

配列と型:サイズが違うと別型

  • 配列は長さを含めて型が決まる。[3]int[4]int は別型で、代入も代入互換も無い。
package main

func main() {
	var a [3]int
	var b [4]int
	// ❌ cannot use b (type [4]int) as type [3]int
}

スライスの初期化・比較:nil と空、比較は不可

宣言のみ(未初期化)のスライスは nil。

一方、リテラル []T{} や make([]T, 0) は空スライス(len=0)だが nil ではない。

スライス同士の比較はできない(== は nil との比較のみOK)。

package main

import "fmt"

func main() {
	var s []int          // nil
	t := []int{}         // 空だが非nil
	u := make([]int, 0)  // 空だが非nil

	fmt.Println(s == nil) // true
	fmt.Println(t == nil) // false
	fmt.Println(u == nil) // false

	// fmt.Println(s == t)  // ❌ slice can only be compared to nil
}

関数呼び出しは値渡し

Goは値渡し。スライスは「ヘッダ(ポインタ等)のコピーなので、

要素の変更は元にも影響(同じ配列を指すため)。

append で再割当が起きると別配列に移り、以降は元に影響しない

package main

import "fmt"

func add1(n int) { n++ }
func touchElem(s []int) { s[0] = 99 }          // 要素変更 → 共有配列が書き換わる

func main() {
	i := 10
	add1(i)
	fmt.Println(i) // 10(値渡し)

	a := []int{1, 2, 3}
	touchElem(a)
	fmt.Println(a) // [99 2 3](値渡し)
}

make で容量(cap) を先取り

スライスの将来の append を見越して cap を大きめに。

package main

import "fmt"

func main() {
	s := make([]int, 0, 5) // len=0 cap=5
	fmt.Println(len(s), cap(s)) // 0 5
	s = append(s, 1,2,3,4,5)
	fmt.Println(len(s), cap(s)) // 5 5(ちょうど)
}

サブスライス式とフルスライス式

package main

import "fmt"

func main() {
	x := make([]string, 0, 5)
	x = append(x, "a","b","c","d") // len=4 cap=5

	y := x[:2]    // サブスライス:len=2 cap=5(共有・危険)
	z := x[:2:2]  // フルスライス:len=2 cap=2(cap制限・安全)

	y = append(y, "Y","Y") // 共有配列に追記→xが汚染され得る
	z = append(z, "Z")     // cap不足→新配列に退避→xは無傷

	fmt.Println("x:", x) // x: [a b Y Y]
	fmt.Println("y:", y) // y: [a b Y Y]
	fmt.Println("z:", z) // z: [a b Z]
}

配列⇄スライス:共有と非共有

配列→スライス:s := arr[:] は同じ配列を共有(書き換えが相互に見える)。

スライス→配列:スライスの要素を変更しても配列には影響しない。

package main

import "fmt"

func main() {
    // 配列からスライスへ
	arr := [4]int{1,2,3,4}
	s := arr[:]     // 共有
	s[0] = 99
	fmt.Println(arr) // [99 2 3 4]
}

copy は共有しない(ディープコピーの一種)

package main

import "fmt"

func main() {
	src := []int{1,2,3}
	dst := make([]int, len(src))
	copy(dst, src)
	dst[0] = 999
	fmt.Println(src) // [1 2 3]
	fmt.Println(dst) // [999 2 3]
}

文字列・rune・UTF-8

文字列はUTF-8のバイト列(不変)。len はバイト数。

rune は int32 の別名。

package main

import (
	"fmt"
	"unicode/utf8"
)

func main() {
	s := "こんにちは"           // 1文字=3バイト、合計15バイト
	fmt.Println(len(s))           // 15(バイト数)
	fmt.Println(utf8.RuneCountInString(s)) // 5(rune数)

	// インデックスはバイト単位
	fmt.Printf("%x\n", s[0])      // e3(先頭バイト)

	// range はrune単位
	for i, r := range s {
		fmt.Printf("i=%d r=%c\n", i, r)
	} // こ→ん→に→ち→は
    
    // 文字列⇄runeスライス
	rs := []rune(s)
	rs[0] = 'さ'
	fmt.Println(string(rs)) // 「さんにちは」
}

文字列のインデックス/スライスは1バイト文字のみで扱うようにする。
日本語などの多バイトでは途中で切ると壊れたUTF-8になる。

推奨:日本語など多バイトを扱うときは []rune に変換してからインデックス/スライスする。

「カンマOK」イディオム

マップ参照:キーの存在確認

package main

import "fmt"

func main() {
	// 在庫数を持つシンプルなマップ
	stock := map[string]int{
		"apple":  3,
		"banana": 0, // 在庫0(キーはある)
	}

	// あるキー
	v, ok := stock["apple"]
	fmt.Println("apple:", v, ok) // 3 true

	// ないキー(ゼロ値が返るが、okがfalseで判別できる)
	v2, ok2 := stock["orange"]
	fmt.Println("orange:", v2, ok2) // 0 false

	// 在庫0と“そもそも存在しない”を区別できるのがポイント
	v3, ok3 := stock["banana"]
	fmt.Println("banana:", v3, ok3) // 0 true
}

集合(Set) をマップで表現

存在だけを表したいので、値型はstruct{}(ゼロサイズ)か bool が定番。

package main

import "fmt"

func main() {
	// 値にboolを使ったSet(存在=true)
	set := map[string]bool{}

	// 追加
	set["go"] = true
	set["rust"] = true

	// 存在確認(キーがなければ false が返る)
	fmt.Println(set["go"])   // true
	fmt.Println(set["java"]) // false

	// 削除
	delete(set, "go")
	fmt.Println(set["go"]) // false
}

構造体(struct)と合成(埋め込み)

Go にはクラスや継承はない。代わりに 構造体の埋め込みやインターフェース、委譲でする。

まとめ(第3章)

  • 配列は長さを含めて型。サイズ違いは別型。

  • スライスはnilと空を区別、比較はnilとのみ。

  • Goの関数は値渡しだが、スライスはヘッダのコピーなので要素変更は共有配列に影響。

  • make で cap 先取り、フルスライスで cap 制限すると安全。

  • 配列→スライスは共有、copy は非共有。

  • 文字列はUTF-8の不変バイト列。インデックス/スライスは1バイト文字のみで、多バイトは []rune/utf8 を使う。

  • カンマOK(map)で存在する値とゼロ値を区別する。

  • クラス/継承はなく、構造体+埋め込み+インターフェースで設計する。

0
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?