この記事の一部は機械翻訳を使ってるよ
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 回だけです。次の図は実行順序を示しています。
例
次の構造を想定する
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
*/