In Go, the fmt.Stringer and error interfaces are used to control how a structure's value is converted to a string and how it implements an error, respectively. These interfaces provide universal ways of logging, outputting, and handling errors, making the code more flexible and understandable.
Background:
From the early versions of Go, the Stringer interface has been key to nicely controlled output of structures. The error interface has proven fundamental for error handling at all levels of code.
Problem:
Often programmers get uninformative output or unexpected error messages because they have not implemented these methods or have done so non-standardly. Additionally, incorrect implementation can lead to recursive output, panics, and unreadable errors.
Solution:
Example code:
package main import "fmt" type User struct { Name string ID int } func (u User) String() string { return fmt.Sprintf("User<%d:%s>", u.ID, u.Name) } type MyError struct { Msg string } func (e MyError) Error() string { return "MyError: " + e.Msg } func main() { u := User{Name: "Bob", ID: 10} fmt.Println(u) // calls String() err := MyError{Msg:"fail"} fmt.Println(err) // calls Error() }
Key features:
Is it mandatory to implement String() or Error() as value methods, or can pointers be used?
Both options are allowed, but implementation on pointer-receiver and value-receiver affects which types of objects the method will work on. Generally, pointer-receiver is used for mutable structures.
func (u *User) String() string {...}
Can fmt.Sprintf be used inside String() or Error()?
Yes, but you must carefully watch for infinite recursion (e.g., outputting a structure of the same type inside String()). It is recommended to avoid using fmt.Print inside String() if String() will be called again internally.
func (u User) String() string { return fmt.Sprintf("%v", u.Name) } // safe
What happens if the Error() method returns an empty string?
Errors with an empty string are treated as valid error values, but logging loses its meaning. The error interface does not define behavior for an empty string, but it is common practice to always provide an informative message.
A developer outputs a structure using %+v without implementing String(), resulting in junk dumps of fields in the logs.
Pros:
Cons:
The team lead makes the team implement String() and Error() for all public structures. As a result, the business logic handles errors centrally, and the admin panel and debug logs become readable.
Pros:
Cons: