-
エラーハンドリング
- 処理実行時のエラー有無は出力の2つ目に出る
- 自動的にハンドリングするわけではないので毎回errのチェックをする必要がある
_, err := func() if err != nil { fmt.Println(err) }
- メモリ解放 - 変数定義の際には完了後クローズを宣言しないとメモリリークに繋がる? - というよりは単純に最後に実行したい処理を書いてるだけか - この`defer`は、呼び出し元の関数(ここでは`main`)がreturnするまで処理を遅延させて、return時に実行してくれるようにできる文法です。
```go
defer f.Close()
```
- errのような変数は使い回す
- 基本的には処理直後のエラーハンドリングでしか使わない
- 終わった次の関数のエラーハンドリングでも同じ変数を使う
-
:=
ではなく=
を使う
- Goの変数名の慣習
- Goでは、できるだけ変数名を短くするという慣習があります
- err(error), cfg(config), msg(message)など
- クラスと構造体の違い
- 同じ
- クラスと構造体は非常に似た概念で、他の言語でクラスが利用される多くの場面でGoでは構造体が利用されます。
- 違い
- クラス変数・クラスメソッドのような、クラスの中で共通の値や処理が、構造体には存在しない。
- クラスと違って「継承」機能がない
- 同じ
-
ポインタ型
-
*
はポイント型なので&aのようなポインタを引数に渡す(変数の値ではなく) - つまり、以下はstringの値が格納されたポインタ型を引数に取る
func sayHello(name *string) { fmt.Printf("Hello, %s\n", *name) }
-
-
ポインターをどのように使うのか
-
- 構造体などのデータ量が大きい型を扱うとき
user := &User{ // &をつけることでポインタ型になる id: "1", name: "John Lennon", age: 30, }
-
- 関数・メソッドの内部などから値を書き換えをしたいとき
type User struct { id string name string age int } func (u *User) IncrementAge() { u.age++ // intなどの数字系の値をインクリメント(+1)する処理 } func main() { user := &User{ // &をつけることでポインタ型になる id: "1", name: "John Lennon", age: 30, } user.IncrementAge() fmt.Println(user.age) // => 31 }
-
- nullが存在しない型にnil(null)を許容させたいとき
- デフォルト値
- string,intなどのGoの基本型には、nil(null)がありません。
- それらの型の変数等は、定義された時点で、デフォルト値を持ちます。
- デフォルト値とは、例えば、stringの場合は空文字列
""
、 intの場合は0
です。 - そのため、それらのデフォルト値とnilを区別できるようにするために、nilを許容しているポインタ型で扱うという方法があります。
- 例外
- ただし、
err
はerror
型であり、Go の基本型(string
,int
など)とは異なる扱いになる - Go において
error
型は インターフェース型 です。インターフェース型は、値が何も設定されていない場合nil
となります。
- ただし、
-
- ポインタの注意点
- 意図せぬ値の変更に注意する
- nil(null)に注意する
- 命名規則
- 構造体
- メンバー変数は命名規則があり、先頭1文字が大文字か小文字かで挙動が変わります。
- 大文字で始めると、パッケージ外からでもアクセスできう
- 小文字で始めると、パッケージ内のみアクセスできる
- メンバー変数は命名規則があり、先頭1文字が大文字か小文字かで挙動が変わります。
- 構造体
-
ファイル内での定義順序
package main import ( "fmt" "log" "os" ) // 1. 定数 const ( AppName = "MyApp" Version = "1.0.0" ) // 2. グローバル変数 var ( debugMode bool logger *log.Logger ) // 3. 構造体の定義 type User struct { ID int Name string Age int } // 4. インターフェース type Greeter interface { Greet() string } // 5. メソッド func (u *User) Greet() string { return "Hello, " + u.Name } // 6. 通常の関数 func add(a, b int) int { return a + b } // 7. init 関数 func init() { debugMode = true logger = log.New(os.Stdout, "LOG: ", log.LstdFlags) } // 8. main 関数 func main() { fmt.Println("Hello, Go!") u := User{ID: 1, Name: "Alice", Age: 25} fmt.Println(u.Greet()) }
-
変数定義
宣言方法 例 使える場所 特徴 :=
a := 10
関数内のみ 型推論あり、変更可 var
var a = 10
関数内・関数外 型指定可能、変更可 const
const Pi = 3.14
関数内・関数外 変更不可
- Go の
const
の制約-
const
は コンパイル時に値が決定できる必要がある。 -
const
にできるのは、数値リテラル, 文字列リテラル,true/false
などの単純な値のみ。 - 配列 (
[N]T
) や スライス ([]T
), マップ (map[T]U
) などの 複合データ型 はconst
にできない。 - つまり、この
favicon
は スライス[]byte
(バイト列) であるため、コンパイル時に確定できずconst
にできない のです。
-
- パブリックアクセスとプライベートアクセス
- それは、先頭の文字が大文字かどうかです。
- 関数でも変数でも型でも、先頭が大文字が始まるものはパッケージ外からアクセス可能で、小文字で始まるものはパッケージ内部専用のものとなります。
- スライスと配列
-
配列:固定長
-
var msgs [2]string
- 番号指定で入力可能
msgs[0] = "Hello" msgs[1] = "こんにちは"
-
-
スライス:可変長
-
var msgs []string
- 独自関数で入力
msgs = append(msgs, "こんにちは")
-
-
- パッケージ管理
- rails:Gemfile, Gemfiie.lock
- go:go.mod, go.sum
- パッケージのimport順序
- 標準->外部->内部の順番
- インターフェースを用いた共通化
- 処理の拡張・共通化を可能にする
- 具体的な挙動を隠蔽して、抽象化・疎結合化する
- テストでモックを作る
# 天気構造体
type WeatherInfo struct {
temperature int
weather string
}
# 天気関数
func (w *WeatherInfo) Report() string {
return fmt.Sprintf("天気: %s, 気温: %v度", w.weather, w.temperature)
}
# 投資構造体
type StockInfo struct {
average int
}
# 投資関数
func (s *StockInfo) Report() string {
return fmt.Sprintf("平均株価: %v円", s.average)
}
# 天気と投資の共通化インターフェース
type Reporter interface {
Report() string
}
# 共通引数の関数
func PrintNews(r Reporter) {
fmt.Println("今日のニュースです。 %s", r.Report())
}
# 利用例
w := &WeatherInfo{
temperature: 20,
weather: "晴れ",
}
s := &StockInfo{
average: 30000,
}
PrintNews(w)
// => 今日のニュースです。 天気: 晴れ, 気温: 20度
PrintNews(s)
// => 今日のニュースです。平均株価: 30000円
-
コマンド引数の定義指定
outputPath := flag.String("o", "./image.png", "Path to output file")
- Closeの目的と対象
- 対象
-
クローズするべきもの
-
resp.Body
(HTTPレスポンスのボディ)- HTTPレスポンスを処理する際に、
resp.Body
を使用した後にClose()
を呼び出すことが重要です。 - これをしないと、接続がクローズされず、リソース(特にソケット)が解放されません。こ
- れがメモリリークや接続の枯渇につながる可能性があります。
- HTTPレスポンスを処理する際に、
-
ファイルやネットワーク接続(
file.Close()
やconn.Close()
)- ファイルやネットワーク接続も同様に、使用後にクローズしないと、リソースが解放されずに、最終的にメモリリークやリソース枯渇が発生することがあります。
-
-
クローズしなくて良いもの
-
一部のリソース管理を自動で行うもの: Goランタイムが自動的に管理するリソース(例えば、
sync.Mutex
やsync.WaitGroup
などのロックオブジェクト)については、手動で閉じる必要はありません。これらは明示的にリソースを解放することが求められないからです。
-
一部のリソース管理を自動で行うもの: Goランタイムが自動的に管理するリソース(例えば、
-
クローズするべきもの
- 目的
-
メモリリーク対策
- リソースを使い終わった後に適切に解放しないと、メモリ上にリソースの参照が残り、ガーベジコレクションがそれらを解放しない場合があります。
- 特に、
resp.Body
のようなストリームリソースはクローズを忘れると、メモリや接続が無限に消費され続ける原因となります。
-
接続の枯渇対策
- HTTPリクエストなどでは、未クローズの接続が積み重なると、サーバーが接続できない状態になったり、システム全体のリソースが枯渇することがあります。
-
メモリリーク対策