関数・レシーバー - Go

最終更新日: 2026-07-25

TOP(About this memo)) > 一覧(Go) > 関数・レシーバー

関数

クロージャ

variadic functions(可変長引数関数)

func Query(db *sql.DB, args ...any) {
  ...
  if len(args) ...
}
opts := []cmp.Option{
  cmpopts.IgnoreFields(entity.User{}, "", ""),
}
if diff := cmp.Diff(want, respObj, opts...); diff != "" {// 配列を可変長引数として展開
  t.Errorf("Compare value is mismatch (-v1 +v2):%s\n", diff)
}

multiple return values

Named return values

func split(sum int) (x, y int) {
	x = sum * 4 / 9
	y = sum - x
	return
}

optional parameter, default value, named parameter

defer

import "fmt"

func main() {
	f(func() {
		defer fmt.Println("callback end")
		fmt.Println("callback")
	})
}

func f(a func()) {
	a()
	fmt.Println("f end")
}

/*
callback
callback end
f end
*/

deferはos.exitが呼ばれると意図しない挙動になる?

panicとの関係

defer 関数 とブロックスコープ

func main() {
	defer println("5")
	{
		defer println("4") // 囲んでいるブロックスコープの末尾ではなく、関数(main関数)の最後に実行される。
		println("1")
	}
	defer println("3")
	println("2")
}
/// 1 -> 2 -> 3 -> 4 -> 5

特殊な関数 init

初期化のパターン

  1. varで初期化する。
var (
	// std is the name of the standard logger in stdlib `log`
	std = New()
)
  1. Init関数で初期化する。
  2. Init関数を使わずに、明示的に自分で関数を作ってmain等から呼ぶ。

var, init, main の順番

  1. importしたパッケージのvarが定義される
  2. importしたパッケージのinit関数が実行される
  3. mainパッケージのvarが定義される
  4. mainパッケージのinit関数が実行される
  5. mainパッケージのmain関数が実行される

レシーバー

メソッドと関数は本質的に同義

func (p Person) Greet(msg string) {
    // ...
}
// 実は、これは下記と等価らしい。
func Person.Greet(p Person, msg string) {
    // ...
}

サンプルコード

import (
	"encoding/json"
	"reflect"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

// func GetFirst[A any](a A, b ...any) A {
// 	return a
// }

// func GetLast(a ...any) any {
// 	return a[len(a)-1]
// }

// func UnmarshalString[S any](jsn string, to *S) (*S, error) {
// 	if err := json.Unmarshal([]byte(jsn), to); err != nil {
// 		return nil, err
// 	}
// 	return to, nil
// }

func Ptr[T any](a T) *T {
	return &a
}


func TestReciever(t *testing.T) {
	t.Run("assert_reciver", func(t *testing.T) {
		assert.Exactly(t, time.Time{}.IsZero(), true)
		assert.Exactly(t, Ptr(time.Time{}).IsZero(), true)
		assert.Exactly(t, Ptr(time.Now()).IsZero(), false)

		assert.Exactly(t, testStruct1("").valueReciever(), true)
		assert.Exactly(t, testStruct1("").pointerReciever(), true)    //値型のレシーバーじゃなくても呼べる。
		assert.Exactly(t, Ptr(testStruct1("")).valueReciever(), true) //ポインタ型のレシーバーじゃなくても呼べる。
		assert.Exactly(t, Ptr(testStruct1("")).pointerReciever(), true)

		// 値レシーバーを実装していると、そのポインターも実装していることになる。
		// しかし、ポインタレシーバーを実装しているときは、その値は実装していることにならない。
		_, ok := reflect.ValueOf(time.Time{}).Interface().(json.Marshaler)
		assert.Exactly(t, ok, true)
		_, ok = reflect.ValueOf(&time.Time{}).Interface().(json.Marshaler)
		assert.Exactly(t, ok, true)
		_, ok = reflect.ValueOf(time.Time{}).Interface().(json.Unmarshaler)
		assert.Exactly(t, ok, false) // これはfalseになる
		_, ok = reflect.ValueOf(&time.Time{}).Interface().(json.Unmarshaler)
		assert.Exactly(t, ok, true)
	})
}

type testStruct1 string

func (t testStruct1) valueReciever() bool {
	return true
}
func (t testStruct1) pointerReciever() bool {
	return true
}

値レシーバーは自身を変更することができない?

type s1 struct {
	f1 string
}

func (s s1) set() {
	s.f1 = "aaaa"
}

func (s *s1) set2() {
	s.f1 = "bbbb"
}

func main() {
	v1 := s1{f1: "0000"}
	v1.set()
	fmt.Println(v1.f1)//0000
	(&v1).set2()
	fmt.Println(v1.f1)//bbbb
}

(参考)レシーバーがポインターと値の2種類それぞれに対して処理を変える必要がある

p := &json.UnmarshalTypeError{}
fmt.Println(errors.As(json.Unmarshal([]byte("{"), &struct{}{}), &p)) 

引数として渡した場合の元の値の変更

package main

import "fmt"

func main() {
  // スライスは参照だが、関数へ渡したスライスの参照先を変更すると、ポインタ自体が置き換わり、元の参照先は変わらない仕様となっている。
	a := []string{}
	fmt.Printf("before call: %p\n", a)
	fmt.Println(a)
	f(a)
	fmt.Printf("after call: %p\n", a)
	fmt.Println(a)
	fmt.Println("_______")

  // mapは参照であり、関数へ渡したmapの参照先を変更すると、元の参照先も変わる。
	b := map[string]string{}
	fmt.Printf("before call: %p\n", a)
	fmt.Println(b)
	f2(b)
	fmt.Printf("after call: %p\n", a)
	fmt.Println(b)
	fmt.Println("_______")

  // 配列の場合は参照でないため、関数側に渡した値を変更しても元の値は変わらない。
	c := [3]string{"a", "b", "c"}
	fmt.Println(c)
	f3(c)
	fmt.Println(c)
	fmt.Println("_______")
}

func f(a []string) {
	fmt.Printf("before append %p\n", a)
	a = append(a, "a")
	fmt.Printf("after append %p\n", a)
}

func f2(a map[string]string) {
	fmt.Printf("before append %p\n", a)
	a["a"] = "a"
	fmt.Printf("after append %p\n", a)
}

func f3(a [3]string) {
	a[2] = "a"
}

/*

before call: 0x590360
[]
before append 0x590360
after append 0xc000014070
after call: 0x590360
[]
_______
before call: 0x590360
map[]
before append 0xc0000160f0
after append 0xc0000160f0
after call: 0x590360
map[a:a]
_______
[a b c]
[a b c]
_______

*/