Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams
func (s *service) registerMethods() {
    s.method = make(map[string]*methodType)
    for i := 0; i < s.typ.NumMethod(); i++ {
        method := s.typ.Method(i)
        mType := method.Type
        if mType.NumIn() != 3 || mType.NumOut() != 1 {
            continue
        if mType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() {
            continue
        argType, replyType := mType.In(1), mType.In(2)
        if !isExportedOrBuiltinType(argType) || !isExportedOrBuiltinType(replyType) {
            continue
        s.method[method.Name] = &methodType{
            method:    method,
            ArgType:   argType,
            ReplyType: replyType,
        log.Printf("rpc server: register %s.%s\n", s.name, method.Name)

what does reflect.TypeOf((*error)(nil)).Elem() mean in this code? I know if mType.Out(0) != reflect.TypeOf((*error)(nil)).Elem() is trying to determine if the method's return type is error not not. But for me, reflect.TypeOf((error)(nil)) intuitively will do the same, but unfortunately not. When I try to compile this code, it says type error is not an expression, what does it mean in this context? Does not reflect.Typeof() accepts a argument of certain type? I found that (*error)(nil) is equivalent to *error = nil. I am confused with this expression.

TL;DR; reflect.TypeOf((*error)(nil)).Elem() is an expression used to obtain the reflect.Type type descriptor of the interface type error. Using reflect.TypeOf(error(nil)) cannot be used for the same purpose (read the why below).

reflect.TypeOf((*error)(nil)).Elem() achieves its goal by using a typed nil pointer value of type *error, passing it to reflect.TypeOf() to get the reflect.Type descriptor of the type *error, and uses Type.Elem() to get the type descriptor of the element (base) type of *error, which is error.

reflect.TypeOf() expects an interface{} value:

func TypeOf(i interface{}) Type

Basically whatever value you pass to reflect.TypeOf(), if it's not already an interface value, it will be wrapped in an interface{} implicitly. If the passed value is already an interface value, then the concrete value stored in it will be passed as interface{}.

So if you try to pass an error value to it, since error is an interface type, the concrete value stored in it will be "repacked" into an interface{} value. The interface type error will not be retained / transferred!

If you pass a nil value of type error, e.g. error(nil), since the interface value itself is nil, it contains no concrete value and type, a nil interface{} value will be passed, that will result in nil reflect.Type returned. Quoting from reflect.TypeOf():

TypeOf returns the reflection Type that represents the dynamic type of i. If i is a nil interface value, TypeOf returns nil.

If you pass a value of type *error (which may be a nil pointer), it's not an interface value, it's a pointer value (a pointer to interface). So it will be wrapped in an interface{} value, and the concrete value stored in it will be of type *error. Using Type.Elem() you can access the pointed type, that is error.

This is one of the rare cases when using a pointer to interface makes sense, and in fact inevitable.

See related questions:

Get the reflect.Kind of a type which is based on a primitive type

What is the difference between reflect.ValueOf() and Value.Elem() in go?

Hiding nil values, understanding why golang fails here

If you pass error(nil), reflect.TypeOf() will receive a nil interface value. Again, this is in my answer. – icza Aug 21, 2021 at 11:27 @WeiWong: this is one of those cases where Go just isn't quite that simple: error is itself an interface type, so that error(nil) turns into the <nil,nil> pair at compile time. Other non-interface types don't have this issue, e.g., type T = *int makes T an alias for *int, so T(nil) turns into <*int,nil> (play.golang.org/p/XO1vjwk031U). It's critical to think of Go in terms of <type,value> pairs at all times. – torek Aug 21, 2021 at 15:21 More or less, yes. All interface values have a <type,value> pair at runtime. The type in the <type,value> pair is supplied by the compiler. When the compiler doesn't have a pre-provided type, the compiler provides nil instead. So any uninitialized variable of type interface I, for any I, is <nil,nil>. The Go authors then chose (for whatever reason) to say that, for any interface value v, v==nil is true if and only if v holds <nil,nil>. That leads to error(nil) "wanting" to be <nil,nil> rather than <error,nil>. – torek Aug 21, 2021 at 15:59 If the Go authors had chosen that, at runtime, the v==nil test should test only the value part of v, not the type part, error(nil) could reasonably be the <error,nil> pair. That would be a different language, with other behaviors also being different; whether it would be better, worse, or equivalent is a matter of taste. – torek Aug 21, 2021 at 16:01 The argument to reflect.TypeOf has to be a value (of type interface{}); error is a type (of type interface { ... } where the ... part is filled in). Other languages sometimes work around this distinction between "type" and "value" by decreeing that types are values (which leads to a different problem), but Go doesn't do that. – torek Aug 22, 2021 at 2:10

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.