在Go中,如何检查函数的返回值是否满足错误接口?

问题描述 投票:0回答:3

我想编写一些代码来检查结构的方法,并对它们进行某些断言,例如,它们返回的最后一个东西应该是error。我尝试了以下示例脚本:

import (
    "context"
    "reflect"
)

type Service struct {
    name string
}

func (svc *Service) Handle(ctx context.Context) (string, error) {
    return svc.name, nil
}

func main() {
    s := &Service{}
    t := reflect.TypeOf(s)

    for i := 0; i < t.NumMethod(); i++ {
        f := t.Method(i).Func.Type()

        f.Out(f.NumOut() - 1).Implements(reflect.TypeOf(error))
    }
}

但是,这会产生一个

./main.go:23:51: type error is not an expression

什么编译,而不是最后两行:

    var err error
    f.Out(f.NumOut() - 1).Implements(reflect.TypeOf(err))

但是,这引起了恐慌:

panic: reflect: nil type passed to Type.Implements

检查最后一个参数是否实现error接口的正确方法是什么?换句话说,如何获得reflect.Type接口的error

go reflection go-reflect
3个回答
0
投票

使用一个指向接口的指针,并获取它的Elem,如下所示:

f.Out(f.NumOut() - 1).Implements(reflect.TypeOf((*error)(nil)).Elem())

0
投票

要在不使用现有错误的情况下获得reflect.TypeOferror,您可以使用此单行代码:

reflect.TypeOf((*error)(nil)).Elem()

[基本上,它首先获取指向错误的指针的类型(*error),然后Elem()将TypeOf“引用”为error的类型。

Playground


0
投票

如果最后一个返回值“应该是”并且error不使用Implements,那还不够,x 实现 e与x is e不相同。

只需检查类型的名称和包路径。对于预声明的类型,包括error,程序包路径为空字符串。


实现error的非错误类型。

type Service struct {
    name string
}

type sometype struct {}

func (sometype) Error() string { return "" }

func (svc *Service) Handle(ctx context.Context) (string, sometype) {
    return svc.name, sometype{}
}

func main() {
    s := &Service{}
    t := reflect.TypeOf(s)

    for i := 0; i < t.NumMethod(); i++ {
        f := t.Method(i).Func.Type()
        rt := f.Out(f.NumOut() - 1)
        fmt.Printf("implements error? %t\n", rt.Implements(reflect.TypeOf((*error)(nil)).Elem()))
        fmt.Printf("is error? %t\n", rt.Name() == "error" && rt.PkgPath() == "")
    }
}

outputs

implements error? true
is error? false

本地声明的名称为error的类型,未实现内置error

type Service struct {
    name string
}

type error interface { Abc() }

func (svc *Service) Handle(ctx context.Context) (string, error) {
    return svc.name, nil
}

type builtin_error interface { Error() string }

func main() {
    s := &Service{}
    t := reflect.TypeOf(s)

    for i := 0; i < t.NumMethod(); i++ {
        f := t.Method(i).Func.Type()
        rt := f.Out(f.NumOut() - 1)
        fmt.Printf("implements error? %t\n", rt.Implements(reflect.TypeOf((*builtin_error)(nil)).Elem()))
        fmt.Printf("is error? %t\n", rt.Name() == "error" && rt.PkgPath() == "")
    }
}

outputs

implements error? false
is error? false

实际内置error

type Service struct {
    name string
}

func (svc *Service) Handle(ctx context.Context) (string, error) {
    return svc.name, nil
}

func main() {
    s := &Service{}
    t := reflect.TypeOf(s)

    for i := 0; i < t.NumMethod(); i++ {
        f := t.Method(i).Func.Type()
        rt := f.Out(f.NumOut() - 1)
        fmt.Printf("implements error? %t\n", rt.Implements(reflect.TypeOf((*error)(nil)).Elem()))
        fmt.Printf("is error? %t\n", rt.Name() == "error" && rt.PkgPath() == "")
    }
}

outputs

implements error? true
is error? true
© www.soinside.com 2019 - 2024. All rights reserved.