1
0

More than 1 year has passed since last update.

この記事誰得? 私しか得しないニッチな技術で記事投稿!

fmt.Printfの表示時にpanicしてもrecoverされる

Posted at

TL; DR

package main

import "fmt"

type Foo struct{}

func (f *Foo) String() string {
	panic("boom")
}

func main() {
	fmt.Printf("%s", &Foo{})
}
結果
%!s(PANIC=String method: boom)

はじめに

おなじみ fmt.Printf では、 %s を使うことで文字列形式で値を出力できます。
%s は、string[]byte だけでなく、Stringer の実装(String() string メソッドを実装している型)に対しても使用可能です。Stringerを %s すると自動的に String が呼び出され文字列化されてから出力されます。

Stringがpanicしたら?

ここで、String メソッド内でpanicした場合の挙動が冒頭のコードです。

func (f *Foo) String() string {
	panic("boom")
}

func main() {
	fmt.Printf("%s", &Foo{})
}

panicが発生するため文字列は得られませんが、代わりにエラーメッセージが表示されます。この際、fmt.Printf自体は成功しています

結果
%!s(PANIC=String method: boom)

別の値を一緒に表示してみると分かりやすいです。
panicはせず、panicした際のエラーメッセージが %s の部分に出力されています。どうやら内部で recoverされて処理が継続しているようです。

func main() {
	fmt.Printf("%s: %s", "print foo struct", &Foo{})
}
print foo struct: %!s(PANIC=String method: boom)

処理系の実装を見に行く

裏を取るために fmt パッケージを読みます。

fmt.Printf%s 形式で Stringerを出力すると、この分岐に入ります。

// ...
case Stringer:
	handled = true
	defer p.catchPanic(p.arg, verb, "String")
	p.fmtString(v.String(), verb)
	return

String 呼び出し時にpanicした場合、deferの catchPanic でrecoverされ、エラーメッセージを文字列として出力しています。これにより、panicをfmtの呼び出し元まで伝播させることなく次の処理に進められます。

おわりに

fmt パッケージはHello worldにも出てくるので簡単なイメージがありましたが、まだまだ知らない仕様があることを痛感しました。精進せねば...

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0