英文:
Expecting nil but getting an interface with a nil value in return, which should be nil
问题
运行它在
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
}
英文:
Run it at
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
}
答案1
得分: 4
只有当内部值和类型都未设置(nil, nil)时,接口值才为nil。特别地,nil接口将始终持有一个nil类型。如果我们将类型为int的指针存储在接口值中,无论指针的值如何,内部类型都将是int:(*int,nil)。因此,即使内部指针为nil,这样的接口值也将是非nil的。
(...)
为了向调用者返回一个正确的nil错误,函数必须显式返回nil:
func returnsError() error {
if bad() {
return ErrBad
}
return nil
}
英文:
This is discussed on the FAQ and the Go Traps website:
> An interface value is nil only if the inner value and type are both unset, (nil, nil). In particular, a nil interface will always hold a nil type. If we store a pointer of type *int inside an interface value, the inner type will be *int regardless of the value of the pointer: (*int, nil). Such an interface value will therefore be non-nil even when the pointer inside is nil.
>(...)
>To return a proper nil error to the caller, the function must return an explicit nil:
>
func returnsError() error {
if bad() {
return ErrBad
}
return nil
}
答案2
得分: 2
Francesc Campoy Flores(来自Google的Go团队)在今年的dotGo大会上谈到了这个特定的问题。
你可以将接口值看作有两个部分:类型和值。因此,类型为*Result
且值为nil
的接口与类型和值都为nil
的接口是不相等的。
你可以通过在比较中指定nil
来修复你的代码:
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
}
英文:
Francesc Campoy Flores (from the Go team at Google) talked about this particular issue in his presentation at dotGo this year.
You can think an interface value has 2 parts; a type and a value. Therefore an interface of type *Result
and value nil
is not equal to an interface with both type and value nil
.
You can fix your code by typing the nil
in the comparison:
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
}
答案3
得分: 0
简而言之:在使用直接返回值或命名返回值的函数中,不要混合使用接口和指针/值。
目标是创建一个错误聚合器,例如:
func checkStatus() *Result {
r := &Result{}
// ... 进行检查并将结果附加到 r.message ...
if r.message == "" {
return nil
}
return r
}
func checkSomething() error {
return runDiagnostics()
}
func runDiagnostics() *Result {
... 做一些操作
// 一切正常
return nil
}
.. 其他地方 .... (使用命名返回值)
if err = checkStatus(); err != nil {
return
}
.. 即使 runDiagnostics() 返回 nil,这里仍然失败 ...
if err = checkSomething(); err != nil {
... err 总是 "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() // <-- 对于直接返回或命名返回值,必须使用 "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
}
英文:
In a nutshell: don't mix interfaces and pointers/values when using direct or named return values in a function.
The goal is to create an error aggregator, e.g:
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"
}
detailed check with a pointer http://play.golang.org/p/WPGy_ooXmP
detailed check with an interface http://play.golang.org/p/JjWxEj9WRX
solution with an error indicator 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
}
solution with an interface 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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论