在https://play.golang.org/p/sl12vfS9vP
package main
import "fmt"
func main() {
err := run()
if err != nil {
fmt.Printf("%#v", err)
}
}
func run() (err error) {
return check()
}
func check() *Result {
return nil
}
type Result struct {
message string
}
func (result *Result) Error() string {
return result.message
}
这在常见问题解答和Go Traps网站上进行了讨论:
只有当内部值和类型都未设置(nil,nil)时,接口值才为nil。特别是,nil接口将始终包含nil类型。如果我们在接口值中存储类型为*int的指针,那么无论指针的值是多少,内部类型都将是*int:(*int,nil)因此,即使内部指针为零,这样的接口值也将为非零
(…)
要向调用方返回正确的nil错误,函数必须返回显式nil:
func returnsError() error {
if bad() {
return ErrBad
}
return nil
}
Francesc Campoy Flores(来自谷歌围棋团队)在今年的dotGo演讲中谈到了这个特殊的问题。
你可以认为一个接口值有两部分;一个类型和一个值。因此,类型为*Result
且值为nil
的接口不等于类型为且值为nil
的接口。
您可以通过在比较中键入nil
来修复代码:
http://play.golang.org/p/E9kro7Fkbr
package main
import "fmt"
func main() {
err := run()
if err != (*Result)(nil) {
fmt.Printf("%#v", err)
}
}
func run() (err error) {
return check()
}
func check() *Result {
return nil
}
type Result struct {
message string
}
func (result *Result) Error() string {
return result.message
}
简而言之:在函数中使用直接或命名返回值时,不要混合接口和指针/值。
目标是创建一个错误聚合器,例如:
func checkStatus() *Result {
r := &Result{}
// ... do checks and append to r.message ...
if r.message == "" {
return nil
}
return r
}
func checkSomething() error {
return runDiagnostics()
}
func runDiagnostics() *Result {
... do something
// all ok
return nil
}
.. somewhere else .... (using named return values)
if err = checkStatus(); err != nil {
return
}
.. this fails even when runDiagnostics() return nil ...
if err = checkSomething(); err != nil {
... err is always "something"
}
用指针进行详细检查http://play.golang.org/p/WPGy_ooXmP
使用接口进行详细检查http://play.golang.org/p/JjWxEj9WRX
带有错误指示器的解决方案http://play.golang.org/p/C30s49GiIN
package main
import "fmt"
func main() {
err := run()
if err != nil {
fmt.Printf("%#v", err)
}
}
func run() (err error) {
_, err = check()
return
}
func check() (*Result, error) {
return nil, nil
}
type Result struct {
message string
}
func (result *Result) Error() string {
return result.message
}
带有接口的解决方案http://play.golang.org/p/bFysxTFVIH
package main
import "fmt"
func main() {
err := run()
if err != nil {
fmt.Printf("%#v", err)
}
}
func run() (err error) {
return check() // <-- for direct return or named return value an "interface{}" must be used
}
// uses an interface - Result
func check() Result {
return nil
}
type Result interface {
Error() string
}
type result struct {
message string
}
func (result *result) Error() string {
return result.message
}