Go言語の関数とメソッド
2026/07/28

定義と戻り値

関数はfuncキーワードで定義します。名前、引数、戻り値の型を書き、returnで値を返します。続く引数が同じ型のとき、型を1回だけ書いてa, b intと省略できます。a int, b intと書いても同じ意味です。型が混ざるときは同型が連続する部分だけ省略し、それ以外はそれぞれ型を書きます。

func add(a, b int) int { // a int, b intと同じ
    return a + b
}

func mix(a, b int, factor float64) float64 { // a int, b int, factor float64
    return float64(a+b) * factor
}
// mix(1, 2, 1.5) → 4.5

複数戻り値とerror

戻り値の型を(T1, T2)のように並べると、複数の値を返せます。呼び出し側はv, err := f()のように左辺の変数の数だけ受け取ります。

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("division by zero")
    }
    return a / b, nil
}

v, err := divide(10, 2)
if err != nil {
    return err
}

defer

deferは関数がreturnするとき(正常終了・panicいずれも)に実行する処理を登録する文です。分岐やループの制御ではなく、関数スコープで後片付けを書くための構文です。

f, err := os.Open(path)
if err != nil {
    return err
}
defer f.Close()

複数のdeferは後入れ先出しで実行されます。引数はdefer登録時に評価され、関数終了時ではありません。ループ内でdeferを書くと、登録は繰り返されますが実行は関数終了時まで遅延します。

for i := 0; i < 3; i++ {
    defer fmt.Println(i) // 登録時のiをキャプチャ → 終了時に2, 1, 0の順
}

if初期化文との組み合わせ

呼び出しとerrのチェックを1行にまとめるときにif初期化文を使います。

if _, err := strconv.Atoi(input); err != nil {
    return err // inputが整数でなければ終了
}

名前付き戻り値

戻り値の型の前に名前を付けると、その名前が関数内の変数として使えます。

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return // この時点のx, yを返す(return x, yと同じ)
}

戻り値に名前を付けているとき、値を書かないreturnはnaked returnと呼ばれ、名前付き戻り値をそのまま返します。名前付き戻り値はドキュメントとして有用ですが、長い関数では何を返しているか追いにくくなります。defer内で名前付き戻り値を書き換えるとreturnの結果が変わるため、意図しない場合があります。

可変長引数

末尾の引数を...Tと書くと可変長引数になります。呼び出し側は値を列挙するか、スライスを...付きで渡します。

func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

sum(1, 2, 3)
sum([]int{4, 5}...)

値渡しと関数値

関数は第一級の値です。引数は値のコピーであり、呼び出し側の変数は関数内で引数を書き換えても変わりません。関数型の変数に代入して、別名から呼び出せます。

func double(n int) int {
    m := n
    n = 0 // コピーnを書き換えても呼び出し側には及ばない
    return m * 2
}

x := 10
y := double(x) // yは20
// xは10のまま

var fn func(int) int = double
z := fn(3) // zは6

関数リテラル

funcに名前を付けず書いた式が関数リテラル(無名関数)です。第一級の値なので、変数に代入してから呼び出せます。

add := func(x, y int) int {
    return x + y
}
sum := add(1, 2) // 3

即時呼び出し

関数リテラルの直後に(引数…)を付けると、変数に代入せずその場で呼び出せます。

n := func(x int) int { return x * 2 }(3) // 6

クロージャ

関数リテラルは外側の関数で宣言した変数を参照できます。参照した変数はリテラルと共有され、リテラルが参照可能な間は保持されます。

func counter() func() int {
    n := 0
    return func() int {
        n++
        return n
    }
}

next := counter()
v1 := next() // 1
v2 := next() // 2

func (c Counter) Inc()と値レシーバで書くと、呼び出し元のc.nは増えません。メソッドは型に紐づく関数で、値レシーバとポインタレシーバでメソッド集合が変わり、インターフェース実装の可否にも影響します。

メソッド宣言

メソッドは型に紐づく関数です。レシーバは関数名の前に書きます。

type Rect struct{ W, H float64 }

func (r Rect) Area() float64 {
    return r.W * r.H
}

レシーバ名は型の短い別名として使われます。同じ型のメソッド集合内で一意である必要があります。

値レシーバとポインタレシーバ

レシーバは値Tまたはポインタ*Tです。

func (r Rect) Perimeter() float64 {
    return 2 * (r.W + r.H)
}

func (r *Rect) Scale(f float64) {
    r.W *= f
    r.H *= f
}

値レシーバではメソッド内の変更はコピーに対してのみ有効です。呼び出し元の値を変更するにはポインタレシーバを使います。

代入可能な値へのアドレス取得

変数pが代入可能なら、値型のレシーバメソッド呼び出しp.Method()は、コンパイラによって(&p).Method()と解釈されることがあります。

type Rect struct{ W, H float64 }
func (r Rect) Area() float64     { return r.W * r.H }
func (r *Rect) Scale(f float64)   { r.W *= f; r.H *= f }

r := Rect{W: 3, H: 4}
r.Scale(2) // 代入可能 → 実質 (*Rect).Scale
fmt.Println(r.Area()) // 24

一方、リテラルやmapの要素など代入可能でない値に対して、ポインタレシーバのメソッドは直接呼べません。

// Rect{W: 1, H: 1}.Scale(2) // コンパイルエラー

ポインタと値の使い分け

次のようなときはポインタレシーバが使われます。

  • メソッドがレシーバのフィールドを変更する
  • レシーバが大きく、コピーを避けたい
  • レシーバがスライスやマップなど、内部データを変更する型である

値レシーバは小さな不変型や、値型として扱いたいときに向きます。大きな構造体では値レシーバのたびに全体がコピーされるため、読み取り専用でもサイズが大きければポインタレシーバを検討します。

ポインタレシーバ*Tのメソッドは、レシーバがnilでも呼び出し自体は可能です。メソッド内でフィールドにアクセスするとパニックになります。値レシーバのメソッドをnil*Tから呼ぶと(*p)に展開されるため、フィールド参照時にパニックします。

var p *Rect
// p.Area()   // 値レシーバ: (*p) の展開で nil ならパニック
// p.Scale(2) // ポインタレシーバ: 呼び出しは可能だが、フィールド参照でパニック

レシーバ型の一貫性

同一の型に対して、値レシーバとポインタレシーバのメソッドを混在させないのが慣習です。混在するとメソッド集合が呼び出し側の型(T*Tか)によって変わり、インターフェース実装の可否にも影響します。値型Tのメソッド集合には値レシーバのメソッドが入り、ポインタ型*Tには値レシーバとポインタレシーバの両方が入ります。

メソッド式とメソッド値

メソッドは関数値として扱えます。

r := Rect{W: 3, H: 4}
fn := r.Area       // メソッド値: レシーバがあらかじめ結び付いている
fmt.Println(fn())

area := Rect.Area  // メソッド式: レシーバを第1引数で渡す
fmt.Println(area(r))

埋め込みとメソッド昇格

構造体に別の型を埋め込むと、その型のメソッドが昇格して外側の型から呼び出せます。埋め込みは継承ではなく、フィールドとして内側の値を持ち、名前を省略してメソッドへ届ける仕組みです。

type Engine struct{}
func (e Engine) Start() {}

type Car struct {
    Engine
}

var c Car
c.Start() // Engine.Start が昇格

外側の型に同名メソッドがある場合、外側が優先されます。インターフェースの埋め込みはメソッド集合の合成であり、構造体の埋め込みとは別の仕組みです。埋め込みはフィールドとメソッドの昇格であり、型の置き換え(代入可能性)を生みません。CarEngineとして使うことはできません。

概要

組み込み関数は事前宣言されており、インポートなしで使えます。スライス・マップ・チャネルの確保や長さ操作など、言語の基本操作を担います。

newとmake

new(T)は任意の型Tのゼロ値を置いた領域を確保し、そのアドレス*Tを返します。メモリの置き場には、関数の呼び出しに合わせて使うスタックと、関数をまたいで値を置けるヒープがあります。ヒープ上の不要な領域はガベージコレクションが回収します。どちらに置くかはGoが決め、プログラマが指定する必要はありません。newはヒープ専用の確保ではありません。

p := new(int) // *int、*p == 0

makeはスライス・マップ・チャネルだけを対象に、内部構造を初期化したTそのものを返します。ポインタではありません。これらの型は内部構造の初期化が必要なためnewではなくmakeを使います。new([]int)nilスライスへのポインタを返すだけで、要素を足せる状態にはしません。

s := make([]int, 0, 10) // len=0, cap=10
m := make(map[string]int)
ch := make(chan int, 1)

lenとcap

lenは配列、スライス、マップ、文字列、チャネルの長さ(またはバッファ内の要素数)を返します。スライスと配列ではcapが容量を返します。

s := make([]int, 2, 5)
fmt.Println(len(s), cap(s)) // 2 5

m := map[string]int{"a": 1}
fmt.Println(len(m)) // 1

appendとcopy

appendは新しいスライスヘッダを返します。cap不足時は新しい元配列が割り当てられることがあります。戻り値を呼び出し元の変数へ代入しないと、呼び出し元から見た長さは変わりません。再割り当て後も古いヘッダを使い続けると、別の配列を指している可能性があります。

s := []int{1}
s = append(s, 2, 3)
s = append(s, []int{4, 5}...)

copy(dst, src)dstへ要素をコピーし、実際にコピーした個数を返します。コピー数は両者の短い方の長さまでです。

dst := make([]int, 2)
n := copy(dst, []int{10, 20, 30})
fmt.Println(n, dst) // 2 [10 20]

deleteとclear

delete(m, key)はマップからキーを削除します。キーが無くてもパニックにはなりません。

m := map[string]int{"a": 1}
delete(m, "a")
delete(m, "missing") // 安全

clear(Go 1.21以降)は型によって動きが違います。マップではすべてのエントリを削除して空にします。スライスでは各要素をゼロ値にし、長さlenはそのままです。

m := map[string]int{"a": 1, "b": 2}
clear(m)
fmt.Println(len(m)) // 0

s := []int{1, 2, 3}
clear(s)
fmt.Println(s, len(s)) // [0 0 0] 3

close

close(ch)はチャネルを閉じ、「もう送らない」ことを示します。送信側が呼ぶのが慣習で、rangeの終了条件にもなります。閉じたチャネルへの送信はパニックです。

ch := make(chan int, 1)
ch <- 1
close(ch)
for v := range ch {
    fmt.Println(v) // 1
}

minとmax

minmax(Go 1.21以降)は、比較可能な同じ型の引数のうち最小・最大を返します。

fmt.Println(min(3, 1, 2)) // 1
fmt.Println(max(3, 1, 2)) // 3

panicとrecover

panicは実行を中断し、recoverdefer内でその中断から復帰します。想定できる失敗は通常errorで返し、panicはプログラムのバグや回復不能な状態向けです。