格式化输出示例
func main() {
err1 := errors.New("这是err1")
err2 := fmt.Errorf("这是err2:%w", err1)
fmt.Printf("err2.Type:%T, err2:%v\n", err2, err2)
}
fmt包源码分析
State接口
// State represents the printer state passed to custom formatters.
// It provides access to the io.Writer interface plus information about
// the flags and options for the operand's format specifier.
type State interface {
// Write is the function to call to emit formatted output to be printed.
Write(b []byte) (n int, err error)
// Width returns the value of the width option and whether it has been set.
Width() (wid int, ok bool)
// Precision returns the value of the precision option and whether it has been set.
Precision() (prec int, ok bool)
// Flag reports whether the flag c, a character, has been set.
Flag(c int) bool
}
Formatter接口
// Formatter is implemented by any value that has a Format method.
// The implementation controls how State and rune are interpreted,
// and may call Sprint(f) or Fprint(f) etc. to generate its output.
type Formatter interface {
Format(f State, verb rune)
}
pp类型
// pp is used to store a printer's state and is reused with sync.Pool to avoid allocations.
type pp struct {
buf buffer
// arg holds the current item, as an interface{}.
arg any
// value is used instead of arg for reflect values.
value reflect.Value
// fmt is used to format basic items such as integers or strings.
fmt fmt
// reordered records whether the format string used argument reordering.
reordered bool
// goodArgNum records whether the most recent reordering directive was valid.
goodArgNum bool
// panicking is set by catchPanic to avoid infinite panic, recover, panic, ... recursion.
panicking bool
// erroring is set when printing an error string to guard against calling handleMethods.
erroring bool
// wrapErrs is set when the format string may contain a %w verb.
wrapErrs bool
// wrappedErr records the target of the %w verb.
wrappedErr error
}
// 重要方法,学习free思想
// free saves used pp structs in ppFree; avoids an allocation per invocation.
func (p *pp) free() {
// Proper usage of a sync.Pool requires each entry to have approximately
// the same memory cost. To obtain this property when the stored type
// contains a variably-sized buffer, we add a hard limit on the maximum buffer
// to place back in the pool.
//
// See https://golang.org/issue/23199
if cap(p.buf) > 64<<10 {
return
}
p.buf = p.buf[:0]
p.arg = nil
p.value = reflect.Value{}
p.wrappedErr = nil
ppFree.Put(p)
}
// 池化思维
var ppFree = sync.Pool{
New: func() any { return new(pp) },
}
buffer类型
// Use simple []byte instead of bytes.Buffer to avoid large dependency.
type buffer []byte
func (b *buffer) write(p []byte) {
*b = append(*b, p...)
}
func (b *buffer) writeString(s string) {
*b = append(*b, s...)
}
func (b *buffer) writeByte(c byte) {
*b = append(*b, c)
}
func (bp *buffer) writeRune(r rune) {
if r < utf8.RuneSelf {
*bp = append(*bp, byte(r))
return
}
b := *bp
n := len(b)
for n+utf8.UTFMax > cap(b) {
b = append(b, 0)
}
w := utf8.EncodeRune(b[n:n+utf8.UTFMax], r)
*bp = b[:n+w]
}
fmt类型,把输出内容写到buffer中
// A fmt is the raw formatter used by Printf etc.
// It prints into a buffer that must be set up separately.
type fmt struct {
buf *buffer
fmtFlags
wid int // width
prec int // precision
// intbuf is large enough to store %b of an int64 with a sign and
// avoids padding at the end of the struct on 32 bit architectures.
intbuf [68]byte
}
pp类型的重要方法
- doPrintf
- printArg
- printValue
- handleMethods,该方法会调用
Formatter接口实例的Format方法,如下:// Is it a Formatter? if formatter, ok := p.arg.(Formatter); ok { handled = true defer p.catchPanic(p.arg, verb, "Format") formatter.Format(p, verb) return }
好文推荐
%p格式化输出的源码
%p的作用:格式化输出指针值。
示例:
func main() {
a := 1
fmt.Printf("%d %p\n", a, &a)
a = 2
fmt.Printf("%d %p\n", a, &a)
}
fmt包源码:
func (p *pp) printArg(arg any, verb rune) {
p.arg = arg
p.value = reflect.Value{}
if arg == nil {
switch verb {
case 'T', 'v':
p.fmt.padString(nilAngleString)
default:
p.badVerb(verb)
}
return
}
// Special processing considerations.
// %T (the value's type) and %p (its address) are special; we always do them first.
switch verb {
case 'T':
p.fmt.fmtS(reflect.TypeOf(arg).String())
return
case 'p':
// 格式化输出指针
p.fmtPointer(reflect.ValueOf(arg), 'p')
return
}
......
}
func (p *pp) fmtPointer(value reflect.Value, verb rune) {
var u uintptr
switch value.Kind() {
// 格式化输出的值必须是这些类型,这些类型都是引用类型
case reflect.Chan, reflect.Func, reflect.Map, reflect.Pointer, reflect.Slice, reflect.UnsafePointer:
// 调用value的Pointer方法
u = value.Pointer()
default:
p.badVerb(verb)
return
}
switch verb {
case 'v':
if p.fmt.sharpV {
p.buf.writeByte('(')
p.buf.writeString(value.Type().String())
p.buf.writeString(")(")
if u == 0 {
p.buf.writeString(nilString)
} else {
p.fmt0x64(uint64(u), true)
}
p.buf.writeByte(')')
} else {
if u == 0 {
p.fmt.padString(nilAngleString)
} else {
p.fmt0x64(uint64(u), !p.fmt.sharp)
}
}
case 'p':
// 输出指针值
p.fmt0x64(uint64(u), !p.fmt.sharp)
case 'b', 'o', 'd', 'x', 'X':
p.fmtInteger(uint64(u), unsigned, verb)
default:
p.badVerb(verb)
}
}
func (v Value) Pointer() uintptr {
k := v.kind()
switch k {
case Pointer:
if v.typ.ptrdata == 0 {
val := *(*uintptr)(v.ptr)
// Since it is a not-in-heap pointer, all pointers to the heap are
// forbidden! See comment in Value.Elem and issue #48399.
if !verifyNotInHeapPtr(val) {
panic("reflect: reflect.Value.Pointer on an invalid notinheap pointer")
}
return val
}
fallthrough
case Chan, Map, UnsafePointer:
return uintptr(v.pointer())
case Func:
if v.flag&flagMethod != 0 {
// As the doc comment says, the returned pointer is an
// underlying code pointer but not necessarily enough to
// identify a single function uniquely. All method expressions
// created via reflect have the same underlying code pointer,
// so their Pointers are equal. The function used here must
// match the one used in makeMethodValue.
return methodValueCallCodePtr()
}
p := v.pointer()
// Non-nil func value points at data block.
// First word of data block is actual code.
if p != nil {
p = *(*unsafe.Pointer)(p)
}
return uintptr(p)
case Slice:
// 输出slice底层数据结构保存的指针
return (*SliceHeader)(v.ptr).Data
}
panic(&ValueError{"reflect.Value.Pointer", v.kind()})
}