Go言語における関数(func)の基礎

JavaScriptを有効にしてください

前書き

関数は、特定のタスクを実行するためのコードの集合体です。
Go言語では、関数はプログラムの基本構成要素の一つであり、その強力な型システムと並行処理のサポートを通じて、効率的で読みやすいコードを書くためのキーとなります。
この記事では、Go言語での関数の定義方法、使い方、および実践的な利用例について、初心者にも分かりやすく解説します。

Go言語での関数の定義方法

Go言語で関数を定義する基本的な構文は次のとおりです。

go
1
2
3
4
func 関数名(パラメータ パラメータの型) 戻り値の型 {
    // 関数の中身
    return 戻り値
}

funcキーワードは関数を定義するために使用されます。関数名はその機能を示唆する名前にすることが推奨されます。パラメータは関数に渡される入力値で、それぞれの型と共に指定されます。
Go言語では、同じ型のパラメータを列挙する際に型を省略して記述することも可能です。
例:x, y int
戻り値の型は、関数が呼び出し元に返す値の型です。戻り値が不要な場合、この部分を省略できます。

関数の例

go
1
2
3
func add(x, y int) int {
    return x + y
}

このadd関数は、2つの整数xyを引数として受け取り、その和を返します。関数の呼び出しは以下のように行います。

go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import "fmt"

func add(x, y int) int {
	return x + y
}

func main() {
	result := add(3, 5)
	fmt.Println(result)
}
実行結果
1
8

複数の戻り値

Go言語の強力な特徴の一つは、関数が複数の戻り値を直接返すことができる点です。

go
1
2
3
func split(sum int) (int, int) {
    return sum / 2, sum % 2
}

このsplit関数は、与えられた整数を2で割った商と余りを返します。複数の戻り値を受け取るには、次のようにします。

go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
package main

import "fmt"

func split(sum int) (int, int) {
	return sum / 2, sum % 2
}

func main() {
	x, y := split(5)
	fmt.Println(x, y)
}
実行結果
1
2 1

Goにおける「複数の戻り値」を受け取らない記法

Goでは関数が 複数の値を返す ことができますが、すべての戻り値を使う必要はありません
特定の戻り値を 無視する(受け取らない) ことが可能です。

すべての戻り値を受け取る場合

通常、関数が複数の戻り値を持つ場合、それらを すべて変数に代入 して受け取ります。

go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import "fmt"

// 2つの値を返す関数
func divide(x, y int) (int, int) {
	quotient := x / y  // 商
	remainder := x % y // 余り
	return quotient, remainder
}

func main() {
	q, r := divide(10, 3)          // 両方の戻り値を受け取る
	fmt.Println("商:", q, "余り:", r) // 出力: 商: 3 余り: 1
}

片方の戻り値を無視する

Goでは、使わない戻り値は _ (アンダースコア)を使って 受け取らない ことができます。

go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
package main

import "fmt"

// 2つの値を返す関数
func divide(x, y int) (int, int) {
	quotient := x / y  // 商
	remainder := x % y // 余り
	return quotient, remainder
}

func main() {
	q, _ := divide(10, 3) // 余りは無視
	fmt.Println("商:", q)  // 出力: 商: 3
}

戻り値を無視するメリット

  1. 不要な変数を宣言しなくて済む
  2. 使わない値を明示的に無視できる
  3. コードがスッキリする

無視してはいけない戻り値

戻り値を無視することで可読性をあげてスッキリしたコードをかけるメリットがある一方で、
無視してはいけない戻り値もあります。

下記コードはファイルの os.Open 関数で os.File のみ取得し、エラーは無視しています。

go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import (
	"fmt"
	"os"
)

func main() {
	file, _ := os.Open("nonexistent.txt") // エラーを無視(良くない)
	defer func() {
		err := file.Close()
		if err != nil {
			fmt.Println(err)
			return
		}
	}()
}
実行結果
1
invalid argument

これはよくありません。
上記例ではファイルが見つからず、Close関数でinvalid argumentのエラーを返しています。

戻り値がエラーを含む場合、エラーチェックを 意図的にスキップ することになるので、適切に処理するべきです。

下記のようにエラーの戻り値はチェックするようにしましょう。

go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
package main

import (
	"fmt"
	"os"
)

func main() {
	file, err := os.Open("nonexistent.txt")
	if err != nil {
		fmt.Println("ファイルを開けませんでした:", err)
		return
	}
	defer func() {
		err := file.Close()
		if err != nil {
			fmt.Println(err)
			return
		}
	}()
}
実行結果
1
ファイルを開けませんでした: open nonexistent.txt: no such file or directory

Goでは、使わない値を _ で無視するのが一般的な書き方 なので、適切に活用しましょう!

Goの名前付き戻り値とは?

Goでは、関数の戻り値に 「名前」 を付けることができます。これは 「名前付き戻り値(named return values)」 と呼ばれます。

通常の関数では、戻り値の型だけを指定しますが、名前付き戻り値を使うと、戻り値の意味を明確にし、関数の中で直接変数として扱うことができます。

名前付き戻り値を使わない場合(通常の戻り値)

まず、通常の関数の戻り値の使い方を見てみましょう。

go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
package main

import "fmt"

// 2つの整数を足し算する関数(通常の戻り値)
func add(a int, b int) int {
	return a + b
}

func main() {
	result := add(3, 5)
	fmt.Println(result) // 8
}

ここでは、add 関数は int 型の戻り値を1つ返していますが、戻り値に名前はついていません。

名前付き戻り値を使った場合

Goでは、戻り値の型だけでなく、戻り値に変数名をつける ことができます。

go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package main

import "fmt"

// 名前付き戻り値を使った足し算関数
func add(a int, b int) (sum int) {
	sum = a + b // ここで変数 `sum` に値を代入
	return      // 変数 `sum` が自動的に返される
}

func main() {
	result := add(3, 5)
	fmt.Println(result) // 8
}

何が変わったの?

  1. add 関数の戻り値 intsum という名前をつけた
  2. return sum と書かなくても、sum に値を入れるだけで関数の外に返せる
  3. return だけで戻り値を返せる(暗黙の return

名前付き戻り値のメリット

戻り値の意味がわかりやすくなる

たとえば、通常の関数だと戻り値が何を意味するのかわかりにくいことがあります。

go
1
2
3
func calculate(x int) (int, int) {
	return x * 2, x * 3
}

この calculate 関数は 2つの整数を返す けど、戻り値が何を意味しているのかわかりにくいですよね?

名前付き戻り値を使うと、意味が明確になります。

go
1
2
3
4
5
func calculate(x int) (double, triple int) {
	double = x * 2
	triple = x * 3
	return
}

これなら、戻り値が何を表しているのかが一目で分かる ようになります!

関数内で戻り値の変数をそのまま使える

例えば、計算の途中で デバッグ(途中経過を確認) したい場合、名前付き戻り値を使うと便利 です。

go
1
2
3
4
5
6
7
func calculate(x int) (double, triple int) {
	double = x * 2
	fmt.Println("途中結果 (double):", double) // 途中経過を確認
	triple = x * 3
	fmt.Println("途中結果 (triple):", triple) // 途中経過を確認
	return // `double` と `triple` がそのまま返る
}

通常の戻り値の関数だと、途中で変数を定義しないとデバッグできないので、コードがシンプルになります。

名前付き戻り値のデメリット

可読性が下がることがある

  • 小さい関数ならシンプルに return x + y のほうが分かりやすい 場合が多い
  • 関数が長くなると、どこで値をセットしているのか分かりにくくなる

シンプルな処理なら普通の return を使うのがベター!

名前付き戻り値を使うべき場面

名前をつけたほうが意味が明確になる場合

go
1
func calculate(x int) (double, triple int) { ... }

処理の途中でデバッグが必要な場合

go
1
2
3
4
5
6
func divide(x, y int) (quotient, remainder int) {
    quotient = x / y
    remainder = x % y
    fmt.Println("商:", quotient, "余り:", remainder)
    return
}

シンプルな計算なら不要

go
1
2
3
func add(a, b int) int {
    return a + b // これで十分!
}

名前付き戻り値のまとめ

方法 メリット デメリット 使うべきケース
通常の戻り値 シンプルでわかりやすい 意味が不明確になることがある 簡単な計算や処理
名前付き戻り値 意味が明確、デバッグしやすい、return 書き忘れ防止 関数が長くなると可読性が落ちる 複数の値を返す場合や、途中で変数を使いたい場合

💡 基本は通常の戻り値を使い、意味が明確になったほうが良い場合は名前付き戻り値を使う!

まとめ

Go言語の関数は、コードの再利用性を高め、複雑なタスクをシンプルにする強力なツールです。型安全性や並行処理のサポートなど、Goの特性を最大限に活用することで、効率的で読みやすいコードを書くことができます。
この記事で紹介した基本的な概念と例を通じて、Go言語での関数の使い方を理解し、あなたのプログラミングスキルをさらに向上させましょう。


スポンサーリンク

共有

もふもふ
著者
もふもふ
プログラマ。汎用系→ゲームエンジニア→Webエンジニア→QAエンジニア