0
0

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の基礎:Rails視点のgolang

Last updated at Posted at 2025-01-31
  • エラーハンドリング

    • 処理実行時のエラー有無は出力の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)
    }
    

  • ポインターをどのように使うのか

      1. 構造体などのデータ量が大きい型を扱うとき
    	user := &User{ // &をつけることでポインタ型になる
    		id:   "1",
    		name: "John Lennon",
    		age:  30,
    	}
    
      1. 関数・メソッドの内部などから値を書き換えをしたいとき
    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
    }
    
      1. nullが存在しない型にnil(null)を許容させたいとき
      • デフォルト値
        • string,intなどのGoの基本型には、nil(null)がありません。
        • それらの型の変数等は、定義された時点で、デフォルト値を持ちます。
        • デフォルト値とは、例えば、stringの場合は空文字列""、 intの場合は0です。
        • そのため、それらのデフォルト値とnilを区別できるようにするために、nilを許容しているポインタ型で扱うという方法があります。
      • 例外
        • ただし、errerror 型であり、Go の基本型(string, int など)とは異なる扱いになる
        • Go において error 型は インターフェース型 です。インターフェース型は、値が何も設定されていない場合 nil となります。

  • ポインタの注意点
    1. 意図せぬ値の変更に注意する
    2. nil(null)に注意する

  • 命名規則
    • 構造体
      • メンバー変数は命名規則があり、先頭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順序
    • 標準->外部->内部の順番

  • インターフェースを用いた共通化
    1. 処理の拡張・共通化を可能にする
    2. 具体的な挙動を隠蔽して、抽象化・疎結合化する
    3. テストでモックを作る
# 天気構造体
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()を呼び出すことが重要です。
        • これをしないと、接続がクローズされず、リソース(特にソケット)が解放されません。こ
        • れがメモリリークや接続の枯渇につながる可能性があります。
      • ファイルやネットワーク接続(file.Close()conn.Close()
        • ファイルやネットワーク接続も同様に、使用後にクローズしないと、リソースが解放されずに、最終的にメモリリークやリソース枯渇が発生することがあります。
    • クローズしなくて良いもの
      • 一部のリソース管理を自動で行うもの: Goランタイムが自動的に管理するリソース(例えば、sync.Mutexsync.WaitGroupなどのロックオブジェクト)については、手動で閉じる必要はありません。これらは明示的にリソースを解放することが求められないからです。
  • 目的
    • メモリリーク対策
      • リソースを使い終わった後に適切に解放しないと、メモリ上にリソースの参照が残り、ガーベジコレクションがそれらを解放しない場合があります。
      • 特に、resp.Bodyのようなストリームリソースはクローズを忘れると、メモリや接続が無限に消費され続ける原因となります。
    • 接続の枯渇対策
      • HTTPリクエストなどでは、未クローズの接続が積み重なると、サーバーが接続できない状態になったり、システム全体のリソースが枯渇することがあります。
0
0
0

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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?