SoFunction
Updated on 2025-03-05

A article will help you understand common errors in reflect reflection in Golang

The reflection of go is very fragile. The premise of ensuring that the reflection code runs correctly is that before calling the method of the reflection object, first ask whether the method you are calling is suitable for all the original types used to create the reflection object.Most of the errors in go reflection come from calling a method that does not fit the current type.(For example, call on an integer reflection objectField()method). Moreover, these errors are usually exposed at runtime, not at compile time, and it's easy to do so if the type we pass is not overwritten in the reflected code.panic

This article introduces the errors that are likely to occur when using go reflection.

There is no type judged before getting the value of Value

for, we have many ways to get its value, such asInt()String()etc. However, these methods have a prerequisite, that is, the underlying layer of the reflection object must be the type corresponding to the method we call, otherwise it willpanic, for example, the following example:

var f float32 = 1.0
v := (f)
// Error: panic: reflect: call of on float32 Value(())

In the above example,fIt's onefloat32Float of type, then we try to passInt()Method to get an integer, but this method can only be used forintReflection object of type, so an error will be reported.

  • Methods involved:Addr, Bool, Bytes, Complex, Int, Uint, Float, Interface; When calling these methods, if the type is incorrect, it willpanic
  • Methods to determine whether a reflective object can be converted to a certain type:CanAddr, CanInterface, CanComplex, CanFloat, CanInt, CanUint
  • How to determine whether other types can be converted:CanConvert, it can be judged whether a reflective object can be converted to a certain type.

passCanConvertMethod to determine whether a reflective object can be converted to a certain type:

// true
(((1.0)))

If we want to convert the reflective object to our custom type, we can useCanConvertTo determine whether it can be converted, then call itConvertMethod to convert:

type Person struct {
   Name string
}

func TestReflect(t *) {
   p := Person{Name: "foo"}
   v := (p)

   // v can be converted to Person type   (t, ((Person{})))

   // v can be converted to Person type   p1 := ((Person{}))
   (t, "foo", ().(Person).Name)
}

illustrate:

  • (Person{})Can be obtainedPersonType of information
  • CanvConvert to(Person{})The specified type

No pointer passed to

If we want to modify the original variable by reflecting the object, we must pass a pointer, otherwise an error will be reported (not considered for the time being).slice, map, Special cases where the structure field contains pointer field):

func TestReflect(t *) {
   p := Person{Name: "foo"}
   v := (p)

   // Error: panic: reflect: using unaddressable value   ("Name").SetString("bar")
}

The reason for this error is thatvIt's onePersonvalue of type, not pointers, so we cannot pass("Name")to modify its fields.

For a reflective object, only a copy of p is obtained, not p itself, so we cannot modify p by reflective object.

Operation on an invalid Value

We have many ways to create, and this kind of method does noterrorReturn value, which means that even if we createWhen an invalid value is passed, there will be no error, but an invalid value will be returned

func TestReflect(t *) {
   var p = Person{}
   v := (p)

   // Person does not exist foo method   // FieldByName returns a reflective object representing Field   v1 := ("foo")
   (t, ())

   // v1 is invalid, only the String method can be called   // Other method calls will be panic   (t, func() {
      // panic: reflect: call of  on zero Value
      (())
   })
}

For this question, we canIsValidMethod to judgeWhether it is valid:

func TestReflect(t *) {
   var p = Person{}
   v := (p)

   v1 := ("foo")
   //Judge whether it is valid through IsValid   if () {
      ("p has foo field")
   } else {
      ("p has no foo field")
   }
}

The Field() method directly panics when the passed index is out of range, without returning an invalid .

IsValidReport reflective objectsvWhether it represents a value. ifvIt is a zero value, then returnfalse. ifIsValidreturnfalse, thenStringEverything else will happen outside ofpanic. Most functions and methods never return invalid values.

When does IsValid return false

ofIsValidThe return value ofWhether it is valid, not whether the value it represents is valid. for example:

var b *int = nil
v := (b)
(())                   // true
(().IsValid())            // false
((v).IsValid()) // false

In the above example,vis valid, it represents a pointer, the object pointed to by the pointer isnil. but()and(v)All are invalid because they represent the object pointed to by the pointer, and the object pointed to by the pointer isnil. We cannot use it basednilTo do any reflection operation.

In other cases IsValid returns false

Except for the above situation,IsValidIn other cases, it will returnfalse

  • Empty reflective value object, obtained bynilThe reflection object created, whichIsValidWill returnfalse
  • The structure reflects the object throughFieldByNameGets a non-existent field, whichIsValidWill returnfalse
  • The structure reflects the object throughMethodByNameObtained a method that does not exist,IsValidWill returnfalse
  • mapReflection object throughMapIndexObtained a non-existent key, whichIsValidWill returnfalse

Example:

func TestReflect(t *) {
   // Empty reflective object   ({}.IsValid())      // false
   // Reflective object created based on nil   ((nil).IsValid()) // false

   s := struct{}{}
   // Get non-existent fields   ((s).FieldByName("").IsValid())  // false
   // Get a method that does not exist   ((s).MethodByName("").IsValid()) // false

   m := map[int]int{}
   // Get the non-existent key of the map   ((m).MapIndex((3)).IsValid())
}

Note: There are other situations that can alsoIsValidreturnfalse, here are only some of the situations listed. When using it, we need to pay attention to whether the reflective object we are using will be invalid.

Modify unmodified values ​​through reflection

forWe can passCanSetMethod to determine whether it can be set:

func TestReflect(t *) {
   p := Person{Name: "foo"}

   // Pass the value to create the emit object,   // Its value cannot be modified because it is a copy   v := (p)
   (t, ())
   (t, (0).CanSet())

   // The following line of code will panic:   // panic: reflect:  using unaddressable value
   // (0).SetString("bar")

   // The pointer reflection object itself cannot be modified.   // The object it points to (that is, ()) can be modified   v1 := (&p)
   (t, ())
   (t, ().CanSet())
}

CanSetReportvWhether the value of ? Addressable only (addressable) and cannot be changed by using values ​​obtained by using unexported structure fields. ifCanSetreturnfalse, callSetor any type of specificsetter(For exampleSetBoolSetInt)WillpanicThe condition of CanSet is addressable.

For reflection objects created by passing values, we cannot modify the original variable through reflection objects.CanSetMethod returnfalseThe exception is that if this value contains a pointer, we can still use that pointer to modify the object it points to.

The object pointed to by the pointer can only be set by the return value of the Elem method.

Calling the Elem method on the wrong Value

ofElem()returninterfaceThe reflective object contains a value or a pointer reflects the value pointed to by the object. If the object's reflectionKindnoor, it will happenpanic. If the reflective object isnil, then returns a zero value.

We know,interfaceTypes actually contain types and data. And we pass it toThe parameters areinterface, so methods are also provided in the reflective object to obtaininterfaceTypes and data of types:

func TestReflect(t *) {
   p := Person{Name: "foo"}

   v := (p)

   // The following line will report an error:   // panic: reflect: call of  on struct Value
   // ()
   (())

   // v1 is a reflection object of type *Person, which is a pointer   v1 := (&p)
   ((), ())
}

In the above example,vIt's onePersontype reflective object, it is not a pointer, so we cannot pass()to get the object it points to. andv1is a pointer, so we can pass()to get the object it points to.

Called a method whose type cannot be called

This is probably the most common type of error, because in go's reflection system, some methods we call return a reflective object of the same type, but this new reflective object may be of a different type. It is also unknown whether the reflected object returned at the same time is valid.

In go, reflection has two major objectsand, they all have some methods that are only applicable to certain specific types, that is, in the reflection design of go, only fortypeandvalueTwo major categories. But there are many types in actual go, such asintstringstructinterfaceslicemapchanfuncetc.

Let's not talk about it for now, weFrom the perspective of the word, abstracting all values ​​of so many types intoAfter that, how do we get information specific to certain types of values? For example, get the value of a certain field in the structure, or call a certain method. This problem is easy to solve. You need to get the structure field, right? Then provide you with aField()Method, you need to call the method, so I will provide you with aCall()method.

But in this way, another problem is, if oursIt's from oneintThe value of type is created, then we callField()The method will happenpanic,becauseintThe value of type is notField()Method:

func TestReflect(t *) {
   p := Person{Name: "foo"}
   v := (p)

   // Get the Name field of the reflected object   (t, "foo", (0).String())

   var i = 1
   v1 := (i)
   (t, func() {
      // The following rank will panic:      // v1 does not have a Field method      ((0).String())
   })
}

As for what methods are specific to certain types, you can refer to the following two documents:

  • Type-specific method
  • Type-specific method

Summarize

  • CallingInt()Float()When you are using the method, you need to make sure that the type of the reflected object is the correct type, otherwisepanic, for example in aflaotCalled on a type of reflection objectInt()The method willpanic
  • If you want to modify the original variable, createWhen you need to pass a pointer to the original variable.
  • ifofIsValid()Method returnfalse, then it is an invalid reflective object, and any method called willpanic,Apart fromStringmethod.
  • For value-based, if we want to modify its value, we cannot call this reflective objectSet*Method, because modifying a copy of a variable has no meaning.
  • At the same time, we cannot passModify unexported fields in the structure, even if we create themWhen the pointer of the structure is passed in.
  • Elem()Only in pointers orinterfaceCalled on a type of reflection object, otherwisepanic, its function is to obtain the reflected object of the object pointed to by the pointer, or to obtain the interfacedatareflective object.
  • andThere are many types of methods, such asField()Call()etc. These methods can only be called on certain types of reflective objects, otherwisepanic

This is the article about this article about a common error in reflect reflection in Golang. For more related Golang reflect reflection content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!