コンテンツにスキップ

Go/return

出典: フリー教科書『ウィキブックス(Wikibooks)』
< Go

Goにおけるreturnキーワードは、関数やメソッドの実行を終了し、呼び出し元に制御を戻すために使用されます。また、関数が値を返すべき場合は、その値を指定するためにも使われます。

基本的な使い方

[編集]
func 関数名(引数) 戻り値の型 {
    // 処理
    return 戻り値
}

主な特徴

[編集]
  • 関数の実行を即座に終了させる
  • 複数の値を返すことが可能
  • 名前付き戻り値(named return values)と組み合わせて使用可能
  • 空のreturn文(戻り値なし)も可能

他のキーワードとの組み合わせとユースケース

[編集]

1. 基本的な値を返す使用法

[編集]
func add(a, b int) int {
    return a + b
}

ユースケース:

  • 計算結果の返却
  • 単純な値の取得
  • 関数チェーンの中間結果

2. 複数の値を返す

[編集]
func divideAndRemainder(a, b int) (int, int, error) {
    if b == 0 {
        return 0, 0, errors.New("0での除算はできません")
    }
    return a / b, a % b, nil
}

ユースケース:

  • エラーと結果の同時返却
  • 複合的な計算結果の返却
  • ステータスと値のペア

3. 名前付き戻り値(named return values)との組み合わせ

[編集]
func divmod(a, b int) (quotient, remainder int, err error) {
	if b == 0 {
		err = errors.New("0での除算はできません")
	} else {
		quotient = a / b
		remainder = a % b
	}
	return // 空のreturn - 名前付き戻り値がそのまま返される
}

ユースケース:

  • 複数の関連した値の返却
  • コード冗長性の削減
  • 戻り値の意図の明確化

4. early return パターン

[編集]
func processUser(user *User) error {
    if user == nil {
        return errors.New("ユーザーがnilです")
    }
    
    if user.ID <= 0 {
        return errors.New("無効なユーザーIDです")
    }
    
    // メイン処理
    return nil
}

ユースケース:

  • ガード句による前提条件チェック
  • エラー条件の早期排除
  • コードのネストレベル削減

5. deferとの組み合わせ

[編集]
func readFile(path string) (string, error) {
    file, err := os.Open(path)
    if err != nil {
        return "", err
    }
    defer file.Close() // returnの前に実行される
    
    data, err := ioutil.ReadAll(file)
    if err != nil {
        return "", err
    }
    
    return string(data), nil
}

ユースケース:

  • リソースクリーンアップの保証
  • エラー条件下でも確実に実行される処理
  • 関数の出口が複数ある場合の共通処理

6. panicとrecoverとの組み合わせ

[編集]
func safeOperation() (result string, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("パニックが発生しました: %v", r)
        }
    }()
    
    // パニックが発生する可能性のある処理
    result = performRiskyOperation()
    return
}

ユースケース:

  • パニックのエラーへの変換
  • 安全な関数境界の確立
  • 予期しない状況からのリカバリ

7. 関数リテラル(クロージャ)からのreturn

[編集]
func createCounter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

ユースケース:

  • 状態を持つ関数の生成
  • カプセル化されたデータアクセス
  • コールバック関数の実装

8. ifステートメントと組み合わせたインラインreturn

[編集]
func findUser(id int) (*User, bool) {
    if id <= 0 {
        return nil, false
    }
    
    user := lookupUser(id)
    if user == nil {
        return nil, false
    }
    
    return user, true
}

ユースケース:

  • 条件付き結果の返却
  • 存在チェックと値取得の組み合わせ
  • 複数条件下での異なる戻り値

9. switchステートメントとの組み合わせ

[編集]
func processStatus(status int) string {
    switch status {
    case 200:
        return "OK"
    case 404:
        return "Not Found"
    case 500:
        return "Internal Server Error"
    default:
        return "Unknown Status"
    }
}

ユースケース:

  • 多分岐条件での戻り値決定
  • ルックアップテーブルの代替
  • 状態コードや列挙型の処理

10. 再帰関数での終了条件

[編集]
func factorial(n int) int {
    if n <= 1 {
        return 1
    }
    return n * factorial(n-1)
}

ユースケース:

  • 再帰の基底ケース定義
  • 数学的アルゴリズムの実装
  • グラフや木構造の走査

11. gotoとの比較/代替

[編集]
// gotoを使った実装
func processWithGoto(input string) string {
    if len(input) == 0 {
        goto errorCase
    }
    
    // 処理
    return "処理完了: " + input

errorCase:
    return "エラー: 入力が空です"
}

// returnを使った同等実装
func processWithReturn(input string) string {
    if len(input) == 0 {
        return "エラー: 入力が空です"
    }
    
    // 処理
    return "処理完了: " + input
}

ユースケース:

  • 複雑な制御フローの簡素化
  • エラー処理の明確化
  • レガシーコードのリファクタリング

12. インターフェース実装の確認

[編集]
func ensureInterface() {
    var _ io.Reader = (*MyType)(nil) // コンパイル時に型チェック
    return
}

ユースケース:

  • インターフェース準拠の静的検証
  • コンパイル時の型安全性保証
  • 暗黙的なインターフェース実装の確認

13. goroutineとの組み合わせ

[編集]
func processAsync(data []int, resultChan chan<- int) {
    go func() {
        sum := 0
        for _, v := range data {
            sum += v
        }
        resultChan <- sum
        return // goroutineの終了
    }()
}

ユースケース:

  • 並行処理の終了
  • バックグラウンド処理の制御
  • 非同期計算の実装

14. forループと組み合わせた早期脱出

[編集]
func findFirst(items []int, predicate func(int) bool) (int, bool) {
    for _, item := range items {
        if predicate(item) {
            return item, true
        }
    }
    return 0, false
}

ユースケース:

  • 検索処理の効率化
  • 条件を満たす最初の要素の取得
  • イテレーションの早期終了

15. 可変長引数関数での活用

[編集]
func sum(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}

ユースケース:

  • 可変数の入力処理
  • ヘルパー関数の実装
  • 汎用計算ロジック

16. 型アサーションとの組み合わせ

[編集]
func extractString(value interface{}) (string, bool) {
    str, ok := value.(string)
    if !ok {
        return "", false
    }
    return str, true
}

ユースケース:

  • 安全な型変換
  • インターフェース値の抽出
  • 多態的な関数の実装

17. エラーラッピングとの組み合わせ

[編集]
func processFile(path string) error {
    content, err := ioutil.ReadFile(path)
    if err != nil {
        return fmt.Errorf("ファイル読み込みエラー: %w", err)
    }
    
    err = processContent(content)
    if err != nil {
        return fmt.Errorf("コンテンツ処理エラー: %w", err)
    }
    
    return nil
}

ユースケース:

  • エラーコンテキストの追加
  • エラー伝播の追跡
  • 詳細なエラー情報の提供

18. レイトバインディングとの組み合わせ

[編集]
type Strategy interface {
    Execute() string
}

func executeStrategy(s Strategy) string {
    return s.Execute()
}

type ConcreteStrategy struct{}

func (c ConcreteStrategy) Execute() string {
    return "戦略を実行しました"
}

ユースケース:

  • ポリモーフィズムの実装
  • 戦略パターン、テンプレートメソッドパターンの実装
  • 実行時の振る舞い選択

使用上の注意点

[編集]
  1. deferとの順序: return文はdefer文より先に評価されるが、実際の関数終了はdefer文の後
  2. goroutine内のreturn: goroutine内のreturnは呼び出し元に値を返さない点に注意が必要
  3. 無限ループからの脱出: 無限ループ内では適切なreturn条件が必要
  4. 副作用を持つ式: return文に副作用を持つ式を含めると、予期しない動作や可読性の低下を招く可能性がある

returnキーワードはGoプログラミングにおける最も基本的で重要なキーワードのひとつです。関数の終了と値の返却という単純な機能ながら、様々なパターンやイディオムと組み合わせることで、効率的で明確なコードを記述するための強力なツールとなります。