perl でいうと、Test::TCP::empty_port() が欲しかったのでちょっと書く。
import ( "net"; "fmt"; ) func empty_port() int { for port := 10000; port < 20000; port++ { addr := fmt.Sprintf("localhost:%d", port); l, err := net.Listen("tcp", addr); if (err == nil) { l.Close(); return port; } } panic("can't listen empty port"); }
空いてそうなポートを順番に Listen してみて、成功したら Close() してから返す。
ここで l は net.Listener 型の値なんだけど、ローカル変数にいれておいてもスコープから外れたときに自動で Close してくれたりはしないようだ。
関数を抜けるときに自動で実行される defer を使うとこんな風にも書ける。
func empty_port() int { for port := 10000; port < 20000; port++ { addr := fmt.Sprintf("localhost:%d", port); l, err := net.Listen("tcp", addr); if (err == nil) { defer l.Close(); return port; } } panic("can't listen empty port"); }
なんかあまり違わないようだけど、今回のようにすぐに return するのではなく、いろいろ処理してからあちこちで return するようなコードの場合は defer で閉じ忘れないようにしておくといいのかな。
[追記]
最初 net.Listen() の直後に
defer l.Close();
を書いていたら、Listen できなかった場合にも Close() を実行しようとして segmentation violation を起こしてしまっていた。
ちなみに defer は複数書けて、後に書いた方から実行される。
import . "fmt"; func main() { defer Println("first defer"); Println("main 1"); defer func() { Println("second defer 1"); Println("second defer 2"); }(); Println("main 2"); }
実行結果
main 1 main 2 second defer 1 second defer 2 first defer