跳转到主要内容
eLearner.app
模块 6 · 第 4 课(共 5)课程中的29/50~10 min
模块课程(4/5)

斯金格接口

fmt.Stringer is probably the most-used standard interface in all of Go's standard library. It's the perfect demonstration of the power of small interfaces.

Definition

Go
type Stringer interface {
    String() string
}

A single signature. Any type that defines String() string satisfies it. And fmt recognizes this interface: when you pass a value to fmt.Print*, if it implements Stringer, the result of String() is used instead of the default representation.

Example

Go
type Point struct{ X, Y int }

func (p Point) String() string {
    return fmt.Sprintf("(%d,%d)", p.X, p.Y)
}

fmt.Println(Point{1, 2})              // (1,2)
fmt.Printf("%v %s\n", Point{3, 4}, Point{5, 6})
// (3,4) (5,6)

The same value prints with custom formatting in %v, %s, Println, Sprint, log, etc. All just by defining one method.

When it makes sense

  • Types that model a domain (ids, codes, coordinates, enumerations).
  • Errors with a human-readable representation (see next lesson on error).
  • Types you log often and for which you want a fixed format.

Not for everything: if the type is simple and the default representation is fine, don't add noise.

Enum types: common pattern with iota

Go
type Status int

const (
    OK Status = iota
    WARN
    ERR
)

func (s Status) String() string {
    switch s {
    case OK:   return "OK"
    case WARN: return "WARN"
    case ERR:  return "ERR"
    default:   return "UNKNOWN"
    }
}

fmt.Println(OK, WARN, ERR)   // OK WARN ERR

Without String(), fmt.Println(OK) would print 0. With String(), it prints the symbolic name. The stringer tool generates this pattern automatically.

Watch out for infinite recursion

Go
type T int
func (t T) String() string {
    return fmt.Sprintf("%d", int(t))  // OK: cast to int breaks the recursion
}

Stringer on a pointer receiver

If String() has a pointer receiver, only *T satisfies Stringer. With fmt.Println(t) (a value), Go looks at the method set of T, doesn't find it, and uses the default format. To avoid this, value receiver is usually used for String() (it's a "view"; it doesn't modify anything).

Try it

锻炼#go.m6.l4.e1
尝试:0加载中...

Implement String() on Point so that it returns (X,Y).

正在加载编辑器...
显示提示

Use `fmt.Sprintf` to format the fields.

3 次尝试后可用的解决方案

锻炼#go.m6.l4.e2
尝试:0加载中...

Implement String() on a type Status (int): 0 → 'OK', 1 → 'WARN', otherwise → 'ERR'.

正在加载编辑器...
显示提示

Switch on the receiver's value; remember to provide a default.

3 次尝试后可用的解决方案

Quiz#go.m6.l4.e3
准备好

Which signature must the method have to satisfy fmt.Stringer?

Go
func (x T) ??? {}
答案选项

Recap

  • fmt.Stringer = interface { String() string }.
  • Implementing it changes how fmt.Print* represents the type.
  • Idiomatic for enums (iota), domain types, codes/identifiers.
  • Never call fmt.Sprintf("%s", t) on t inside String() (recursion).
  • Convention: value receiver for String() (no mutation, avoids "method-set surprises").