GoLang 関数

この記事の一部は機械翻訳を使ってるよ

Golang シリーズ

Hello GoLang: https://blog.yexca.net/ja/archives/154
GoLang (var and const) 変数と定数: https://blog.yexca.net/ja/archives/155
GoLang (func) 関数: この記事
GoLang (slice and map) スライス: https://blog.yexca.net/ja/archives/160
GoLang (OOP) オブジェクト指向: https://blog.yexca.net/ja/archives/162
GoLang (reflect) リフレクション: https://blog.yexca.net/ja/archives/204
GoLang (struct tag) 構造タグ: https://blog.yexca.net/ja/archives/205
GoLang (goroutine) ゴルーチン: https://blog.yexca.net/ja/archives/206
GoLang (channel) チャンネル: https://blog.yexca.net/ja/archives/207

複数の戻り値

Go 関数は複数の値を返すことができる

匿名の戻り

package main

import "fmt"

func swap(a, b int) (int, int) {
    return b, a
}

func main() {
    var x, y = swap(10, 20)
    fmt.Println(x, y)
}

/* 出力
 * 20 10
 */

パラメータ名を返す

package main

import "fmt"

func swap(a, b int) (x int, y int) {
    x = b
    y = a
    return
}

func main() {
    var x, y = swap(10, 20)
    fmt.Println(x, y)
}

上記の戻り値は同じ型であり、組み合わせることができる

package main

import "fmt"

func swap(a, b int) (x, y int) {
    x = b
    y = a
    // 如果不给 x,y 赋值,默认为0
    return
}

func main() {
    var x, y = swap(10, 20)
    fmt.Println(x, y)
}

init と main

init はどのパッケージにも、あるいは同じパッケージに複数回出現することができますが、1 つだけ記述することをお勧めします。

main は package main 内にのみ存在でき、パッケージにはこの関数がなければなりません。

これら 2 つの関数は予約関数であり、定義時にパラメーターや戻り値を持つことはできません。

Go プログラムは自動的に init() と main() を呼び出す

プログラムの実行

プログラムの初期化と実行はすべて main パッケージから始まります。同じパッケージが複数のパッケージでインポートされた場合でも、インポートされるのは 1 回だけです。次の図は実行順序を示しています。

image

次の構造を想定する

hello
  -- InitLib1
    -- lib1.go
  -- InitLib2
    -- lib2.go
  main.go

内容は以下のとおりです

lib1.go

package InitLib1

import "fmt"

func init() {
    fmt.Println("lib1 init")
}

lib2.go

package InitLib2

import "fmt"

func init() {
    fmt.Println("lib2 init")
}

main.go

package main

import (
    "fmt"
    // ここでのアンダースコアはパッケージの別名です。別名でない場合はインポートは呼び出されず、コンパイルは成功しません。
    _ "hello/InitLib1"
    _ "hello/InitLib2"
)

func init() {
    fmt.Println("main init")
}

func main() {
    fmt.Println("main")
}

運用結果

lib1 init
lib2 init
main init
main

Lib1 パッケージを Lib2 にインポートすると、main コードは変更されません。

lib1.go

package InitLib1

import (
    "fmt"
    _ "hello/InitLib2"
)

func init() {
    fmt.Println("lib1 init")
}

main 結果を実行

lib2 init
lib1 init
main init
main

lib2 は 1 回だけ出現する

他のパッケージ関数を呼び出す

上記の例では、エイリアスとして _ を使用していますが、これは匿名であり、対応するパッケージ メソッドを呼び出すことはできません。

lib1 に関数を追加する

lib1.go

package InitLib1

import (
    "fmt"
    //_ "hello/InitLib2"
)

// 他のパッケージから呼び出す前に、最初の文字を大文字にする必要があります
func Lib1Test() {
    fmt.Println("lib1 test")
}

func init() {
    fmt.Println("lib1 init")
}

main.go

package main

import (
    "fmt"
    // パッケージにエイリアスを付ける
    mylib1 "hello/InitLib1"
    _ "hello/InitLib2"
)

func main() {
    // alias.method 経由で呼び出す
    mylib1.Lib1Test()
    fmt.Println("main")
}

/*
 * 出力
 * lib1 init
 * lib2 init
 * lib1 test
 * main
 */

または . を直接使用して

main.go

package main

import (
    "fmt"
    // エイリアスが . に変更されました。
    . "hello/InitLib1"
    _ "hello/InitLib2"
)

func main() {
    // 直接使用する
    Lib1Test()
    fmt.Println("main")
}

推奨されません。 2 つのパッケージに同じ名前の関数がある場合、あいまいさが生じます。

ポインタ

C ポインタに似ている

数を呼び出すときにパラメータを渡す方法は、値渡しとポインタ(参照)渡しの 2 つがあります。デフォルトでは、この記事の最初のコードのように値の受け渡しが使用されます。

変数に対応するメモリアドレスを取得するには & を使用します

package main

import (
    "fmt"
)

func main() {
    var a int
    fmt.Printf("%x", &a)
}

参照渡しはメモリアドレスを関数に渡し、関数の変更は実際のパラメータに影響します。これはスワップ関数でもあり、今回はポインタを使用しています。

package main

import "fmt"

func swap(a, b *int) {
    var tmp = *a
    *a = *b
    *b = tmp
}

func main() {
    x, y := 10, 20
    swap(&x, &y)
    fmt.Println("x =", x, "y =", y)
}

defer

defer ステートメントは、遅延関数と呼ばれる関数の呼び出しをスケジュールするために使用されます。その機能は次のとおりです。

  • 占有されているリソースを解放する
  • 例外をキャッチして処理する
  • 出力ログ

try…catch…finally の finally に似ています

ファイルのオープン/クローズ、ロックの取得/解放、接続/切断などのペア操作を処理するためによく使用され、エラーや早期復帰が発生した場合でもリソースが適切に解放され、実行が保証されます。

関数内に複数の defer ステートメントがある場合、それらはスタックと同様に LIFO (後入れ先出し) の順序で実行されます。

package main

import "fmt"

func deferDemo() {
    defer fmt.Println("1")
    defer fmt.Println("2")
    defer fmt.Println("3")
    defer fmt.Println("4")
}

func main() {
    deferDemo()
}

/*
 * 出力
 * 4
 * 3
 * 2
 * 1
 */

recover

ランタイム パニック例外がトリガーされると、プログラムはクラッシュします。recover は、例外をキャプチャする Java の try…catch と同様に、ランタイム パニックを「インターセプト」するために使用される組み込み関数です。

recover は defer によって呼び出される関数内でのみ有効です

package main

import "fmt"

func deferDemo(i int) {
    var arr [10]int
    // エラーインターセプションはエラーの前に設定されます
    defer func() {
        // エラーメッセージを傍受するように recover を設定する
        err := recover()
        if err != nil {
            fmt.Println(err)
        }
    }()
    arr[i] = 10
}

func main() {
    deferDemo(10)
    fmt.Println("main code")
}

/*
 * 出力
 * runtime error: index out of range [10] with length 10
 * main code
 */
This post is licensed under CC BY-NC-SA 4.0 by the author.
最終更新 2025-01-28 14:57 +0900

Hugo で構築されています。 | テーマ StackJimmy によって設計されています。