忽略“String() string”方法打印Go类型

5
如果我在go语言中定义了以下类型:
type myType ...

func (m myType) String() string { ... }

如何使用默认表示打印此类型(使用各种fmt函数)(即,而不是调用String())?我想要做的是像这样:

func (m myType) String() string {
    // some arbitrary property
    if myType.isValid() {
        // format properly
    } else {
        // will recurse infinitely; would like default
        // representation instead
        return fmt.Sprintf("invalid myType: %v", m)
    }
}
3个回答

6

fmt.Stringer 是默认格式,当你使用 %v 时调用。如果您想要使用 Go 语法,请使用 %#v

或者,您可以完全绕过 fmt 中的反射,并根据需要格式化输出。

func (m myType) String() string {
    return fmt.Sprintf("{Field: %s}", m.Value)
}

如果myType的底层类型是数字、字符串或其他简单类型,则在打印时将其转换为底层类型:
func (m mType) String() string {
    return fmt.Sprint(int(m))
}

4

使用%#v替代%v

这样不会调用String()方法,但如果你实现了它,它将会调用GoString()方法。


这个问题在Effective Go中有涉及。OP应该将值转换为底层类型,然后使用%v格式。请参考我的回答。 - Ainar-G
1
@Ainar-G 你的答案对于类型别名来说是微不足道的,但对于例如 type myType struct { a myOtherType} 这样的情况就不那么直接了。Effective Go 中似乎也没有任何提示表明 %#v 不是 OP 想要的东西。 - nos

2

如果您想让底层类型的String正常工作或者您的类型是类型别名,使用%#v格式不是正确的答案。

Effective Go中所解释的,只需将其转换回它所代表的类型即可:

type Foo int

func (f Foo) String() string {
    if f == 0 {
        return "foo"
    }
    return fmt.Sprintf("%v", int(f)) // N.B.
}

func main() {
    fmt.Println(Foo(0))
    fmt.Println(Foo(42))
}

Playground

编辑:如其他评论所指出,如果您的类型是一个结构体,使用%#v格式似乎是唯一的方法,除非将其转换为具有相同字段的匿名结构体类型。


只有当myType是类型别名时,这才真正有所帮助。更常见的情况是它本身就是一个结构体。 - JimB
有没有不需要显式转换的方法来实现这个?我的用例是我有一个结构体,创建一个匿名版本并在两者之间进行转换会很麻烦。 - joshlf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接