Go言語入門 学習メモ 1 変数、型、定数

# はじめに
船井総研デジタルのoswです。業務でGo言語を使うことになったのでこれから学習していきます。その備忘録です。参考になる方がいらっしゃれば幸いです。

# 対象読者
- これからGo言語を学習する方
- 既に他の言語で基本構文を学習されている方

# 学習環境
学習環境は次のようになっています。この環境の構築メモは下記記事にまとめてあります。ご興味がある方はご参照ください。
- Windows 11 / Home
- VSCode
- go version go1.19.2 windows/amd64
- git version 2.38.0.windows.1

https://qiita.com/fsd-osw/items/0e569b0b828455cdef9e

# Goの概要
概要をさらっと触れます。
- コンパイラ型の静的型付け言語
- クロスコンパイルが可能
    - 以下の環境変数を設定することで各環境に合わせて実行ファイルを生成
        - GOOS
        - GOARCH
    - 組み込みも可能
- 標準ツールが豊富
    - コンパイル、ドキュメント生成、静的解析などが行える
- 並行処理が簡単にできる
- ライブラリが豊富
    - https://pkg.go.dev/std
- 2009年にオープンソースで公開
    - 2012年に正式バージョン Go1.0を公開
    - 最新ver Go1.19.2(2022/10/17時点)
    - 半年ごとにバージョンアップ

C / C++ / Javaなど他の言語を既に触れている方は少々戸惑うかもしれません。Goではコンパイラが文末にセミコロンを自動的に挿入するため、明示的に記述する必要はないようです。他にもif文で()を省略、switchではbreakは書かないなど、Goらしく書くには慣れが必要かもしれません。

ただ、慣れてしまえばスッキリして見える気がするのでそれまでは写経するに尽きる、という感じでしょうか。

# コードテンプレート
学習する上で使うテンプレートです。これを流用してコードを記述していくことにします。Goのプログラムはmainパッケージのmain関数がエントリポイントになっているようです。
```go:テンプレート
// main関数が所属するファイルはmainパッケージに含ませる必要がある
package main

// fmtパッケージをインポート
import "fmt"

/*
 * エントリポイント
 */
func main() {
    // コードを記述
}
```

# 変数
```go:構文
// 宣言
var 変数 型
var 変数1, 変数2 型

// 初期化
var 変数 型 = 値
var 変数    = 値 // 型推論が働き、型を省略可能
変数       := 値 // := を使った初期化をするとvar, 型を省略可能

// 複数の変数を初期化
var 変数1, 変数2 型 = 値1, 値2
var 変数1, 変数2    = 値1, 値2
変数1, 変数2       := 値1, 値2
// var 変数1, 変数2 型 = 値1 宣言した分だけ値を渡す必要があり、これはコンパイルエラーになる

// 変数をまとめて定義
var (
	変数 型 = 値
	変数    = 値
	変数1, 変数2 型 = 値1, 値2
	変数1, 変数2    = 値1, 値2
	// 変数 := 値 var()の中では := は使えず、コンパイルエラーになる
)
```

```go:サンプルコード
package main

import "fmt"

// 関数の外では := は使えず、varを使って定義する必要がある
var gNum = 100
// gStr := "test" コンパイルエラー

func main() {

	// 宣言
	var num1 int
	num1 = 0

	// 初期化
	var num2 int = 1
	var num3     = 2
	num4        := 3

	// 複数の変数を初期化
	var str1, str2 string = "aaa", "bbb"
	var str3, str4        = "ccc", "ddd"
	str5, str6           := "eee", "fff"

	// 変数をまとめて定義
	var (
		num5 int    = 10
		num6        = 11
		str7 string = "uuu"
		str8        = "vvv"
		str9, str10  string = "www", "xxx"
		str11, str12        = "yyy", "zzz"
		// str13 := "test" var()の中では := は使えず、コンパイルエラーになる
	)

	fmt.Printf("宣言\n")
	fmt.Printf("num1: %d\n\n", num1)

	fmt.Printf("初期化\n")
	fmt.Printf("num2: %d\n", num2)
	fmt.Printf("num3: %d\n", num3)
	fmt.Printf("num4: %d\n\n", num4)

	fmt.Printf("複数の変数を初期化\n")
	fmt.Printf("str1, str2: %s, %s\n", str1, str2)
	fmt.Printf("str3, str4: %s, %s\n\n", str3, str4)

	fmt.Printf("変数をまとめて定義\n")
	fmt.Printf("num5: %d\n", num5)
	fmt.Printf("num6: %d\n", num6)
	fmt.Printf("str5: %s\n", str5)
	fmt.Printf("str6: %s\n", str6)
	fmt.Printf("str7,  str8 : %s, %s\n", str7, str8)
	fmt.Printf("str9,  str10: %s, %s\n", str9, str10)
	fmt.Printf("str11, str12: %s, %s\n", str11, str12)
}
```

```go:実行結果
宣言
num1: 0

初期化
num2: 1
num3: 2
num4: 3

複数の変数を初期化
str1, str2: aaa, bbb
str3, str4: ccc, ddd

変数をまとめて定義
num5: 10
num6: 11
str5: eee
str6: fff
str7,  str8 : uuu, vvv
str9,  str10: www, xxx
str11, str12: yyy, zzz
```

## ゼロ値
変数を初期化せず宣言だけを行うと型にしたがい、自動的にゼロ値とよばれるものが入るようです。

|型|ゼロ値|
|:-|:-:|
|数値型(int, floatなど|0|
|真偽値型|false|
|文字列型|"" (空文字列)|
|ポインタ|nil|
|配列|全ての要素を0で初期化|
|構造体|全てのフィールドを0で初期化|
|スライス|nil|
|マップ|nil|

## 画面への出力
今回、出力にはfmt.Printf()を使っていますが、fmtパッケージをインポートすると各種PrintX()が使えるようになるようです。

Print(), Println()は変数を","区切りで指定するので個人的に少々違和感があります。その使い方だとCっぽく使えるPrintfがしっくりきたため使っています。各ライブラリの違いは下記記事でまとめて下さっていますのでご興味があればご参照ください。

https://qiita.com/taji-taji/items/77845ef744da7c88a6fe

# 関数名の先頭文字
関数名の先頭文字はリンケージを意味しているようで、それぞれ外部パッケージへ公開 / 非公開を意味しているそうです。fmt.Println()のつもりでfmt.println()とするとエラーになる、ということのようです。

|先頭文字|意味|
|:-:|:-|
|大文字|外部パッケージへ公開|
|小文字|外部パッケージへ非公開|

```go:サンプルコード
package main

// importも()でまとめる事ができる。","で区切る必要はない
import (
	"fmt"
)

func main() {
	// fmt.println("test") コンパイルエラー: undefined: fmt.printlnとなる
	fmt.Println("test")
}
```

# 型
## 整数型
| 型           |  bit  | 符号  | 最小値               | 最大値               |
| :----------- | :---: | :---: | :------------------- | :------------------- |
| int8         |   8   | あり  | -128                 | 127                  |
| int16        |  16   | あり  | -32768               | 32767                |
| int32 / rune |  32   | あり  | -2147483648          | 2147483647           |
| int64        |  64   | あり  | -9223372036854775808 | 9223372036854775807  |
| uint8 / byte |   8   | なし  | 0                    | 255                  |
| uint16       |  16   | なし  | 0                    | 65535                |
| uint32       |  32   | なし  | 0                    | 4294967295           |
| uint64       |  64   | なし  | 0                    | 18446744073709551615 |

## 浮動小数点型
| 型      |  bit  |
| :------ | :---: |
| float32 |  32   |
| float64 |  64   |

## 文字列型
| 型     |
| :----- |
| string |

## 真偽値型
| 型   |
| :--- |
| bool |

## 複素数型
| 型         |
| :--------- |
| complex64  |
| complex128 |

## 環境依存
サイズを指定する、符号なしを指定するなど、特別な理由がない限りはintで整数を使うことが推奨されている様子。

| 型      | bit(32bit OS) | bit(64bit OS) |
| :------ | :--------------: | :--------------: |
| int     |        32        |        64        |
| uint    |        32        |        64        |
| uintptr |        32        |        64        |

# キャスト
Goでは明示的にキャスト(型変換)をしないとコンパイルエラーになるようです。
```go:構文
// 値v, 型Tのとき
var 変数 T = T(v)

// シンプルに書くと
変数 := T(v)
```

```go:サンプルコード
package main

import "fmt"

func main() {
	f := 5.1

	// コンパイルエラー: cannot use f (variable of type float64) as type int in variable declaration
	// var i int = f

	i := int(f)
	fmt.Println(i)
}
```

float -> intで少数を切り捨て、整数を格納すると思いきや、コンパイルエラーになりました。しっかりキャストしてやると実行できます。
```go:実行結果
5
```

# 定数
定数を定義するには`const`を使います。文字(character)、文字列(string)、boolean、数値(numeric)のみ使用可能で、:=による初期化はできない。
なお、命名に関してですが、パスカルケースで記述するのが一般的なようです。ただ、その際はリンケージを意識し、先頭が大文字の場合は外部パッケージへ公開するという意味を含んでいることも忘れないようにしたいところです。
```go:構文
const World = "World"
const Truth = true
const Tax   = 0.1

// ()でまとめることもできる
const (
    World = "World"
    Truth = true
    Tax   = 0.1    
)
```

```go:サンプルコード
package main

import "fmt"

const (
	World = "World"
	Truth = true
	Tax   = 0.1
)

func main() {
	fmt.Println(World)
	fmt.Println(Truth)
	fmt.Println(Tax)
}

```

```go:実行結果
World
true
0.1
```

# おわりに
今回はここまでです。