1 of 57

2. 基本構文

The Go gopher was designed by Renée French.

The gopher stickers was made by Takuya Ueda.

Licensed under the Creative Commons 3.0 Attributions license.

2 of 57

注意事項と免責事項

  • 利用は個人の学習の範囲内でお願いします
    • この資料は個人の学習を目的とした利用に限ります
    • この資料を使った講義等を行う場合は事前に@tenntennに�許可を得てください
    • 生成AIを用いたサービスに学習させ、それを配布する行為を禁じます
  • 免責事項
    • この資料を元に発生した問題、この資料を参考にして作成した�ソフトウェア等に基づく問題について作成者は責任を負いません

2

3 of 57

質問について

  • Gophers Slackの#japanチャンネルでお願いします
    • Slackへの招待URL: https://invite.slack.golangbridge.org/
    • @tenntennまでメンションをください
    • ※質問への回答はすぐに行われるわけではないので予めご了承ください

3

4 of 57

上田拓也

Go ビギナーズ

Go Conference

Google Developer Expert (Go)

一般社団法人 Gophers Japan 代表理事

バックエンドエンジニアとして日々Goを書いている。Google Developer Expert (Go)。一般社団法人Gophers Japan代表。Go Conference主催者。大学時代にGoに出会い、それ以来のめり込む。人類をGopherにしたいと考え、Goの普及に取り組んでいる。複数社でGoに関する技術アドバイザーをしている。マスコットのGopherの絵を描くのも好き。

作者

5 of 57

【PR】企業向け研修や技術アドバイザー

  • Goに関する研修・講義
    • 初学者から中級者以上向けの講義を行えます
    • プログラミング言語Go完全入門をベースにカスタマイズ可能です
  • 技術アドバイザー
    • PRのレビュー
    • 週1回1時間程度のMeetやZoomでの相談
  • 依頼方法
    • フォームからお問い合わせください
    • 短期・長期のどちらでも契約が可能です
    • 講義・ハンズオン、技術相談などを組み合わせることも可能です
    • 実績等はhttps://tenntenn.dev/ja/job/をご覧ください

6 of 57

目次

  1. 言語仕様
  2. 変数
  3. 定数
  4. 演算子
  5. 制御構文

7 of 57

2.1. 言語仕様

7

8 of 57

Goの言語仕様

  • Goとして満たすべき仕様がまとめられている
    • https://go.dev/ref/spec
    • 文法などGoが何ができて何ができないかをまとめたルール
    • 文法はEBNF(拡張バッカス・ナウア記法)で記述されている
  • EBNF(拡張バッカス・ナウア記法)

9 of 57

Goの学習と言語仕様

パターン

で学ぶ

ルール

を学ぶ

背景

から学ぶ

初学者

中級者

上級者

どう書くかで学ぶ

未知の書き方に抵抗感

覚える量が増えていく

言語仕様や文法を学ぶ

ルールで未知を克服

リリース後に知る

機能が生まれる背景

リリース前から把握

機能の深い理解が可能

プログラミング言語Go完全入門では

言語仕様や文法にも触れて解説します

10 of 57

Goファイルの構成

  • 1つのGoファイルは以下のように構成されている
    • 青字は予約語(Keywords)

package main

import "fmt"

var msg = "hello"

func main() {

fmt.Println(msg)

fmt.Println("世"+"界")

}

関数宣言

変数宣言

インポート宣言

パッケージ宣言

他に定数宣言と型宣言がある

英語の文法のようにソースコードが

どのように構成されているか知ろう

11 of 57

宣言(Declarations)

  • 識別子とGoの各機能を紐づける
    • https://go.dev/ref/spec#Declarations_and_scope
    • 定数、型、型パラメータ、変数、関数、ラベル、パッケージ
    • consttypevarなどの予約語を付けて宣言を書く
      • 例:var n int // 変数宣言
  • 識別子(Identifiers)
    • 変数名や関数名など言語機能に紐づけられた名前の総称
    • プログラミング言語自体を話題にする場合に登場する用語
  • 予約語(Keywords)
    • https://go.dev/ref/spec#Keywords
    • 識別子に使えない語
    • Goのプログラム上重要な働きをする
    • 25個存在する

12 of 57

文と式

  • 文(Statement)
    • 代入文やif文、for文など
    • 関数などのブロックを構成するもの
  • 式(Expression)
    • 四則演算式や関数呼び出し式など
    • 整数リテラルや識別子(変数名や関数名など)なども含む
    • 文を構成するもの
    • 単一の式からなる文(式文)も存在する
      • 戻り値を受け取らない関数呼び出しなど
      • 例:println("hello")

13 of 57

知識を深める

  • 自分のコードを理解する
    • 他者に質問できる知識が自分の知識
    • 生成AIや補完が書いたものも理解した上で活用する
    • 知識をアウトプットして深める
  • 当たり前は本当に当たり前か?
    • 他の言語の知識を借りて理解していないか?
      • 例:構造体はクラス → ぜんぜん違う
    • その理解は本当に正しいか?
      • 例:x := 100は右辺がint型だから変数xint型� → 右辺はint型ではない(詳しくは定数で扱う)

14 of 57

2.2. 変数

14

15 of 57

変数

  • 値を保持する領域
    • 識別子(変数名)で識別される
    • 処理中に一時的に値を保存しておくために必要
    • メモリ上にある値なのでマシンが止まると消える(揮発性)
    • プログラムの実行が終わると消える

15

hoge

100

fuga

true

piyo

"piyo"

ここではメモリマップを描いて

メモリ上の状態を把握しやすくしている

16 of 57

変数と型

    • どういう種類の値かを表すもの
      • 整数、浮動小数点数、真偽値、文字列 など
      • 自分で作ることも可能(ユーザ定義型)
    • 変数の型:どういう種類の値が入るのかを表したもの
  • 動的型付け言語
    • プログラム実行時に型を検証する
    • 変数に型がなく、なんでも代入できる
  • 静的型付け言語
    • Goは静的型付け言語
    • コンパイル時に型を検証する
    • 変数に型がある、型が違うと代入できない

16

17 of 57

静的型付けの利点

  • 実行前に型の不一致を検出できる
    • コンパイルが通れば型の不一致が起きない
    • 型の不一致によるバグは見つけづらい問題
  • 曖昧なものはエラーになる
    • 暗黙の型変換がない
      • 1 + "2" => "12"
    • 浮動小数点数と整数の演算など見つけづらいバグが起きにくい
  • 型推論がある
    • 明示的に型を書く必要がない場合が多い

17

18 of 57

変数宣言

  • 変数宣言のいろいろ
    • 予約語のvar(variableの略)を用いる
      • 予約語:識別子(名前)に使用できない語
      • Goの宣言文(変数、関数、型、定数など)予約語で始まる

18

// 変数宣言と代入が一緒

var n int = 100

// 変数宣言と代入が別

var n int

n = 100

// 型を省略(int型になる)

var n = 100

// varを省略

// 関数内のみでしかできない

n := 100

// まとめる

var (

n = 100

m = 200

)

関数内では

:= で省略する場合が多い

19 of 57

使っていない変数

  • 使っていない変数(識別子)はコンパイルエラー
    • バグである場合が多い
    • 無駄な処理を行わない

package main

func main() {

x := 100

println(x)

y := 200 // 使ってないのでコンパイルエラー

println(x) // println(y)の間違い

}

20 of 57

組み込み型

  • 最初から使える型
    • 8や16はその型を表現するために必要なサイズ
      • int8は整数を表すために8ビット使用している
    • byteuint8runeint32、anyはinterface{}の型エイリアス
    • comparableは型制約のみに使えるインタフェース(ジェネリクス)

20

整数

int, int8, int16, int32, int64,

uint, uint8, uint16, uint32, uint64, uintptr, byte, rune

浮動小数点数

float32, float64

複素数

complex64, complex128

文字列

string

真偽値

bool

インタフェース

error, any, comparable

組み込みで使える識別子を

宣言済み識別子と呼ぶ

21 of 57

変数のゼロ値

  • Goの変数は明示的な初期化をしなくても使える
    • ゼロ値という値が設定され、型によって違う

ゼロ値

intやfloat64などの数値

0

string

""

bool

false

errorなどのインタフェース

nil

22 of 57

変数の利用

Hello, Worldのプログラムをprintln関数に渡す文字列を一度変数に格納してから渡して出力してみよう

22

package main�

func main() {

println("Hello, 世界")

}

23 of 57

2.2. 定数

23

24 of 57

定数

  • 値の変わらないもの
    • コンパイル時から値が変わらないもの
    • リテラルで記述されることが多い
      • リテラル = そのものを記述したもの(名前のないそのもの)

種類

数値リテラル

100, 1.5

文字列リテラル

"hoge"

ルーンリテラル

'A', '世'

25 of 57

2進数・8進数・16進数表記

  • 10進数表記以外にも対応
    • 数字の前にプリフィックスをつける

10進

2進

8進

16進

100

0b1100100

0144

0o144

0x64

10.5

-

-

0x1.5p+03

26 of 57

数字の区切り

  • 桁を区切りたい場合などに使える
    • 数値リテラルに_を入れても無視される
    • 数値リテラルの可読性を上げるために導入された

5_000_000_000_000_000

0b_1010_0110

3.1415_9265

27 of 57

定数式

  • 定数のみからなる演算式
    • コンパイル時に計算される

種類

演算結果

四則演算

100 + 200

300

シフト演算

1 << 2

4

文字列結合

"Hello, " + "世界"

"Hello, 世界"

関係演算/論理演算

!(10 == 20)

true

28 of 57

名前付き定数

  • 定数に名前をつけて宣言する
    • 変数宣言と似たように定数に名前がつけれる

// 型のある定数

const n int = 100

// 型のない定数

const m = 100

// 定数式の使用

const s = "Hello, " + "世界"

// まとめる

const (

x = 100

y = 200

)

29 of 57

定数の型

  • 型を持たない定数
    • 型を明示しない場合に定数は型を持たず、デフォルトの型を持つ
    • リテラル表記 / 定義済み(組み込み)名前つき定数 / 組み込み関数
    • 組込み関数の戻り値は型なしの定数になる場合がある

種類

型なしの定数

デフォルトの型

整数

100

int

浮動小数点数

1.5

float64

複素数

1+4i, complex(0, 4)

complex128

ルーン

'A', '世'

rune

文字列

"hoge"

string

真偽値

true

bool

30 of 57

定数のデフォルトの型

  • 変数に代入される場合はデフォルトの型になる
    • 型推論ではデフォルトの型として推論される
    • 型が指定されている変数への代入はその型になる
    • 変数や型のある定数との演算はそれらの型になる
      • 型変換できない場合はコンパイルエラーになる

package main

func main() {

// 定数は型を持たないので無限の精度になる

const n = 10000000000000000000 / 10000000000000000000

var m = n // mはint型

println(m) // 1

}

31 of 57

なぜ型なしの定数が必要か?

  • 定数をただの数字のように扱いたい
    • 型なしの定数のみ暗黙に型変換される
    • 無限精度な整数や浮動小数点数(実装上の制約はある
      • int型やfloat64型のように精度が決まっていない
    • 型なしの定数が無いと型変換ばかりのコードになる
      • 例)time.Duration(30) * time.Second
        • 30 * time.Second // 30が型なしの定数なためこう書ける
  • シンプルに使うための裏の複雑さ
    • Goのユーザはシンプルにつかえるが、その裏の設計は複雑
    • https://go.dev/blog/constants
    • Simplicity is Complicated by Rob Pike

32 of 57

【TRY】定数の利用

Hello, Worldのプログラムをprintln関数に渡す文字列を定数として定義してから出力してください。

32

package main�

func main() {

println("Hello, 世界")

}

33 of 57

右辺の省略

  • 名前付き定数定義の右辺が省略できる
    • グループ化された名前付き定数で用いられる
    • 名前付き定数の右辺を省略できる
    • 省略された定数定義の右辺は、最後の省略されていない定数の右辺と同じになる(型も含む)

func main() {

const (

a = 1 + 2

b

c

)

fmt.Println(a, b, c)

}

全部3になる

右辺を省略できる

34 of 57

iota

  • 組み込みで用意された特別な定数
    • ConstSpecのインデックスを表す
    • 型なしの整数

ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .

ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .

const (

a, b = iota, iota // 0, 0

c = iota // 1

d, e = iota, iota // 2, 2

)

ConstDecl

定数宣言

ConstSpec

0

1

2

定数宣言の文法(EBNF)

35 of 57

iota利用した連番

  • 定数に連続した値を付与するために用いる
    • 列挙体のように用いる
    • 値に意味がなく、それぞれが違う値であれば良い
    • 値に意味があったり、DBなどに保存する場合はiotaを使わない
      • 順番が変わると値が変わってしまう
    • 2つめ以降のConstSpecの右辺を省略できる
      • 1つめの右辺と同じになる

const (

StatusOK = iota // 0

StatusNG // 1(StatusNG = iotaと同じ意味)

)

36 of 57

iotaを利用したフラグ

  • iotaを定数式で使う
    • iota分だけ左シフトさせるとiota番目のビットだけ1になる
    • フラグを表現する際に用いる
      • ビット演算するのに便利

const (

FlagA = 1 << iota // 1

FlagB // 2 (FlagB = 1 << iotaと同じ意味)

FlagC // 4

FlagD // 8

)

37 of 57

2.4. 演算子

37

38 of 57

算術演算

38

演算子

説明

利用例

+,-

符号

+100, -100

+

加算

1+2

-

減算

1-1

/

割り算

4/2

*

掛け算

3*5

%

余り

5%2

39 of 57

代入演算

39

演算子

説明

利用例

=

変数への代入

a = 100

:=

変数の初期化と代入

a := 100

+=,-= など

演算と代入

i += 2

++

i += 1と同義

i++

--

i -= 1と同義

i--

i++i--は式ではなく文

参考:https://go.dev/play/p/2yHzHgS28JT

40 of 57

ビット演算

40

演算子

説明

利用例

|

論理和

0x10|0x01

&

論理積

0x1&0xf

^

否定

^0x3

^

排他的論理和

0xc^0x3

&^

論理積の否定

0xc&^0x3

<<

左に算術シフト

0x1<<4

>>

右に算術シフト

0x4>>1

41 of 57

論理演算

41

演算子

説明

利用例

||

または

a || b

&&

かつ

a && b

!

否定

!a

42 of 57

比較演算

42

演算子

説明

利用例

==

等しいかどうか

a == b

!=

等しくないか

a != b

<

abより小さい

a < b

<=

ab以下

a <= b

>

abより大きい

a > b

>=

ab以上

a >= b

43 of 57

アドレス演算

43

演算子

説明

利用例

&

ポインタを取得

&a

*

ポインタが指す値を取得

*ptr

※ポインタ演算はできない

44 of 57

チャネル演算

44

演算子

説明

利用例

<-

チャネルへの送受信

ch<-100, <-ch

45 of 57

その他の演算子 ①

45

演算子

説明

利用例

.

フィールドやメソッドのアクセス

参考:https://go.dev/ref/spec#Qualified_identifiers

v.field, v.method()

[,]

インデクサや型パラメータなど

array[0], slice[10]

{,}

ブロックやコンポジットリテラルなど

func main() {}

:

コンポジットリテラルなど

m := map[string]int{

"cat": 100,

}

46 of 57

その他の演算子 ②

46

演算子

説明

利用例

...

配列リテラルの長さの省略、可変長引数、スライスの展開など

func f(values ...any) {

}

~

型制約においてUnderlying typeを表す

参考:https://pkg.go.dev/cmp#Ordered

詳細は 15. ジェネリクス(型パラメタ)で扱う

type Ordered interface {

~int | ~int8 | ~int16 | ~int32 | ~int64 |

~uint | ~uint8 | ~uint16 | ~uint32 |

~uint64 | ~uintptr |

~float32 | ~float64 |

~string

}

47 of 57

【TRY】演算子の利用

  • 演算の結果を変数に代入する例

47

// 整数の演算

n := 100 + 200

// 変数を使った演算

m := n + 100

// 文字列の足し算

msg := "hoge" + "fuga"

48 of 57

2.5. 制御構文

48

49 of 57

if文 ①

  • 条件式の値(trueまたはfalse)で分岐する
    • elseは条件に当てはまらない(条件式がfalse)場合
    • elseの後にifをさらにつけることができる
    • elseelse ifは省略可能

49

if x == 1 {

println("xは1")

} else if x == 2 {

println("xは2")

} else {

println("xは1でも2でもない")

}

x == 1

true

x == 1

false

x == 2

true

x == 2

false

50 of 57

条件分岐:if

■ Goのifの特徴

50

// ()がいらない

if a == 0 {

}

// できない

if a == 0

{

}

// できない

if (a == 0) fmt.Println(a)

// 代入文を書くif a := f(); a > 0 {� fmt.Println(a)�} else {� fmt.Println(2*a)�}

aifelse

ブロックで使える

※ Goはコンパイル時に後ろに改行があると、自動でセミコロンを付与する

※ 改行の前が{などの文字だと差し込まない

51 of 57

条件分岐:switch

  • breakがいらない(書いても良いが普段は書かない)
  • caseに式が使える

51

switch a {

case 1, 2:

fmt.Println("a is 1 or 2")

default:

fmt.Println("default")

}

switch {

case a == 1:

fmt.Println("a is 1")

}

caseをまたぐ際には、

fallthroughを使う

何もしないとbreakになる

大量のif-elseをつなぐより

見通しがよくなる

1または2の時

trueを省略している

52 of 57

繰り返し:for

  • 繰り返しはfor(およびfor range)しかない
    • do whileに対応する機能はない
      • コードが難解になりやすい

52

// 初期値;継続条件;更新文

for i := 0; i <= 100; i = i + 1 {

}

// 継続条件のみ

for i <= 100 {

}

// 無限ループ

for {

}

// rangeを使った繰り返し

for i, v := range []int{1, 2, 3} {

}

53 of 57

繰り返し:for range

  • 特定の型の値に要素を繰り返す
    • 配列、スライス、文字列、マップ、チャネル、整数
    • 特定のシグネチャ(引数・戻り値)を持つ関数(イテレータ)
    • マップは繰り返しの順序が実行の度に変わる可能性がある

53

// 変数iが0から99まで繰り返し

for i := range 100 { /* 略 */}

// スライス

for i, v := range []int{1, 2, 3} { /* 略 */ }

// マップ

m := map[string]int{"dog": 1, "cat":99}

for k, v := range m { /* 略 */ }

配列・スライス・マップは3. 関数と型を参照。チャネルは9. ゴールーチンとチャネルを参照。

文字列は12. テキスト処理を参照。イテレータは16. イテレータを参照。

54 of 57

break文によるループの抜け出し

  • breakを用いるとループから抜け出せる

54

// breakによる無限ループの脱出

func main() {

var i int

for {

if i%2 == 1 {

break

}

i++

}

}

// ラベル指定のbreak

func main() {

var i int

LOOP: // for文にラベルをつける

for {

switch {

case i%2 == 1:

break LOOP

}

i++

}

}

Goにはgotoも存在する

55 of 57

continueによるループの継続

  • continue文を用いると次の繰り返しに移る
    • 条件にマッチしない場合はcontinue文で飛ばすという使い方が多い

55

func main() {

var i int

for i := range 10 {

if i%2 == 1 {

continue

}

fmt.Println(i)

}

}

56 of 57

【TRY】奇数と偶数

1-100までの数値のうち、奇数の場合は「数値-奇数」、具偶数の場合は、「数値-偶数」という表示を行うプログラムを作成してください。条件分岐にはifを用いてください。

56

$ go run main.go

1-奇数

2-偶数

3-奇数

4-偶数

...

実行イメージ

switchを使ったパターンもやってみよう

57 of 57