Skip to content

Reflexão

Reflexão é um mecanismo que permite verificar a estrutura da própria linguagem em tempo de execução. Ela pode lidar com alguns problemas de forma flexível, mas também traz desvantagens óbvias, como problemas de desempenho, etc. Em Go, a reflexão está intimamente relacionada com interface{}. Em grande parte, onde quer que interface{} apareça, haverá reflexão. A API de reflexão em Go é fornecida pelo pacote padrão reflect.

Interfaces

Antes de começar, vamos entender brevemente duas interfaces localizadas no pacote runtime. Em Go, interfaces são essencialmente structs. O Go divide as interfaces em duas categorias principais em tempo de execução: uma categoria são interfaces sem conjuntos de métodos, e a outra categoria são interfaces com conjuntos de métodos. Para interfaces que contêm conjuntos de métodos, elas são representadas em tempo de execução pela struct iface a seguir:

go
type iface struct {
   tab  *itab // Contém tipo de dados, tipo de interface, conjunto de métodos, etc.
   data unsafe.Pointer // Ponteiro para o valor
}

Para interfaces sem conjuntos de métodos, elas são representadas em tempo de execução pela struct eface, como segue:

go
type eface struct {
   _type *_type // Tipo
   data  unsafe.Pointer // Ponteiro para o valor
}

Ambas estas structs têm tipos de struct correspondentes no pacote reflect. iface corresponde a nonEmptyInterface:

go
type nonEmptyInterface struct {
  itab *struct {
    ityp *rtype // Tipo de interface estática
    typ  *rtype // Tipo concreto dinâmico
    hash uint32 // Hash do tipo
    _    [4]byte
    fun  [100000]unsafe.Pointer // Conjunto de métodos
  }
  word unsafe.Pointer // Ponteiro para o valor
}

E eface corresponde a emptyInterface:

go
type emptyInterface struct {
   typ  *rtype // Tipo concreto dinâmico
   word unsafe.Pointer // Valor apontado pelo ponteiro
}

Para estes dois tipos, a definição oficial é muito clara:

  • nonEmptyInterface: nonEmptyInterface is the header for an interface value with methods
  • emptyInterface: emptyInterface is the header for an interface{} value

Acima foi mencionado o termo "tipo concreto dinâmico",原文为dynamic concrete type. Primeiro, Go é uma linguagem de tipo 100% estática. O termo "estático" é refletido no fato de que o tipo de interface abstrata manifestado externamente é imutável, enquanto "dinâmico" significa que o tipo de implementação concreta armazenado na base da interface pode mudar. Até aqui, entender o princípio simples das interfaces até este ponto é suficiente para satisfazer o aprendizado subsequente de reflexão.

Ponte

No pacote reflect, há o tipo de interface reflect.Type para representar tipos em Go, e o tipo de struct reflect.Value para representar valores em Go:

go
type Type interface {
    ...

    Name() string

  PkgPath() string

  Size() uintptr

  String() string

  Kind() Kind

    ...
}

type Value struct {

   typ *rtype

   ptr unsafe.Pointer

   flag

}

O código acima omite muitos detalhes. Por enquanto, basta saber da existência destes dois tipos. Todas as operações relacionadas à reflexão em Go são baseadas nestes dois tipos. O pacote reflect fornece duas funções para converter tipos em Go nos dois tipos acima para realizar operações de reflexão: a função reflect.TypeOf:

go
func TypeOf(i any) Type

E a função reflect.ValueOf:

go
func ValueOf(i any) Value

Podemos ver que os tipos de parâmetros de ambas as funções são any, que é um alias de interface{}. Se quisermos realizar operações de reflexão, precisamos primeiro converter seu tipo para interface{}. É por isso que foi mencionado anteriormente que não há reflexão sem interface vazia. De forma não rigorosa, a interface vazia é a ponte que conecta o sistema de tipos do Go com a reflexão. A figura abaixo descreve vividamente este processo.

TIP

No texto a seguir, para conveniência, usaremos uniformemente o alias any para substituir interface{}.

Núcleo

Em Go, há três leis clássicas de reflexão. Combinando com o conteúdo explicado acima, elas são muito fáceis de entender:

  1. A reflexão pode converter variáveis do tipo interface{} em objetos de reflexão
  2. A reflexão pode restaurar objetos de reflexão em variáveis do tipo interface{}
  3. Para modificar um objeto de reflexão, seu valor deve ser definível

Estas três leis são o núcleo da reflexão em Go. Quando precisamos acessar informações relacionadas ao tipo, precisamos usar reflect.TypeOf. Quando precisamos modificar valores de reflexão, precisamos usar reflect.ValueOf.

Tipo

reflect.Type representa tipos em Go. Usando a função reflect.TypeOf(), podemos converter uma variável em reflect.Type. O exemplo de código é o seguinte:

go
func main() {
  str := "hello world!"
  reflectType := reflect.TypeOf(str)
  fmt.Println(reflectType)
}

O resultado é:

string

Kind

Para Type, o Go usa internamente reflect.Kind para representar tipos básicos em Go. Sua essência é um inteiro sem sinal uint:

go
type Kind uint

O pacote reflect enumera todos os tipos básicos em Go usando Kind, como mostrado abaixo:

go
const (
   Invalid Kind = iota
   Bool
   Int
   Int8
   Int16
   Int32
   Int64
   Uint
   Uint8
   Uint16
   Uint32
   Uint64
   Uintptr
   Float32
   Float64
   Complex64
   Complex128
   Array
   Chan
   Func
   Interface
   Map
   Pointer
   Slice
   String
   Struct
   UnsafePointer
)

O tipo Kind implementa apenas o método String() da interface Stringer. Este tipo tem apenas este método. O valor de retorno do método String() vem de um slice interno, como mostrado abaixo. Esta写法 parece muito com map à primeira vista, mas na verdade é uma escrita especial em Go: sintaxe de inicialização de índice (index expressions in slice literals):

go
var kindNames = []string{
   Invalid:       "invalid",
   Bool:          "bool",
   Int:           "int",
   Int8:          "int8",
   Int16:         "int16",
   Int32:         "int32",
   Int64:         "int64",
   Uint:          "uint",
   Uint8:         "uint8",
   Uint16:        "uint16",
   Uint32:        "uint32",
   Uint64:        "uint64",
   Uintptr:       "uintptr",
   Float32:       "float32",
   Float64:       "float64",
   Complex64:     "complex64",
   Complex128:    "complex128",
   Array:         "array",
   Chan:          "chan",
   Func:          "func",
   Interface:     "interface",
   Map:           "map",
   Pointer:       "ptr",
   Slice:         "slice",
   String:        "string",
   Struct:        "struct",
   UnsafePointer: "unsafe.Pointer",
}
go
type Type interface{
    Kind() Kind
}

Através de Kind, podemos saber qual tipo básico o valor armazenado na interface vazia é. Por exemplo:

go
func main() {
    // Declara uma variável do tipo any
  var eface any
    // Atribui valor
  eface = 100
    // Obtém seu tipo através do método Kind
  fmt.Println(reflect.TypeOf(eface).Kind())
}

Resultado:

int

Elem

go
type Type interface{
    Elem() Type
}

Usando o método Type.Elem(), podemos determinar o tipo de elemento armazenado por uma estrutura de dados do tipo any. Os tipos de parâmetros subjacentes aceitáveis devem ser um dos seguintes: ponteiro, slice, array, canal, mapa. Caso contrário, ocorrerá panic. Aqui está um exemplo de código:

go
func main() {
  var eface any
  eface = map[string]int{}
  rType := reflect.TypeOf(eface)
    // key() retorna o tipo de reflexão da chave do map
  fmt.Println(rType.Key().Kind())
  fmt.Println(rType.Elem().Kind())
}

Output:

string
int

Um ponteiro também pode ser entendido como um contêiner. Para ponteiros, usar Elem() obterá o tipo de reflexão do elemento apontado. Exemplo de código:

go
func main() {
  var eface any
    // Atribui ponteiro
  eface = new(strings.Builder)
  rType := reflect.TypeOf(eface)
    // Obtém o tipo de reflexão do elemento apontado pelo ponteiro
  vType := rType.Elem()
    // Output do caminho do pacote
  fmt.Println(vType.PkgPath())
    // Output do seu nome
  fmt.Println(vType.Name())
}
strings
Builder

Para arrays, slices e canais, o uso é semelhante.

Size

go
type Type interface{
    Size() uintptr
}

Através do método Size, podemos obter o tamanho em bytes ocupado pelo tipo correspondente. Exemplo:

go
func main() {
  fmt.Println(reflect.TypeOf(0).Size())
  fmt.Println(reflect.TypeOf("").Size())
  fmt.Println(reflect.TypeOf(complex(0, 0)).Size())
  fmt.Println(reflect.TypeOf(0.1).Size())
  fmt.Println(reflect.TypeOf([]string{}).Size())
}

Resultados:

8
16
16
8
24

TIP

Usar unsafe.Sizeof() pode alcançar o mesmo efeito.

Comparable

go
type Type interface{
    Comparable() bool
}

Através do método Comparable, podemos determinar se um tipo pode ser comparado. Exemplo:

go
func main() {
  fmt.Println(reflect.TypeOf("hello world!").Comparable())
  fmt.Println(reflect.TypeOf(1024).Comparable())
  fmt.Println(reflect.TypeOf([]int{}).Comparable())
  fmt.Println(reflect.TypeOf(struct{}{}).Comparable())
}

Output:

true
true
false
true

Implements

go
type Type interface{
    Implements(u Type) bool
}

Através do método Implements, podemos determinar se um tipo implementa uma determinada interface:

go
type MyInterface interface {
  My() string
}

type MyStruct struct {
}

func (m MyStruct) My() string {
  return "my"
}

type HisStruct struct {
}

func (h HisStruct) String() string {
  return "his"
}

func main() {
  rIface := reflect.TypeOf(new(MyInterface)).Elem()
  fmt.Println(reflect.TypeOf(new(MyStruct)).Elem().Implements(rIface))
  fmt.Println(reflect.TypeOf(new(HisStruct)).Elem().Implements(rIface))
}

Resultado:

true
false

ConvertibleTo

go
type Type interface{
    ConvertibleTo(u Type) bool
}

Usando o método ConvertibleTo, podemos determinar se um tipo pode ser convertido para outro tipo especificado:

go
type MyInterface interface {
  My() string
}

type MyStruct struct {
}

func (m MyStruct) My() string {
  return "my"
}

type HisStruct struct {
}

func (h HisStruct) String() string {
  return "his"
}

func main() {
  rIface := reflect.TypeOf(new(MyInterface)).Elem()
  fmt.Println(reflect.TypeOf(new(MyStruct)).Elem().ConvertibleTo(rIface))
  fmt.Println(reflect.TypeOf(new(HisStruct)).Elem().ConvertibleTo(rIface))
}

Output:

true
false

Valor

reflect.Value representa o valor da interface de reflexão. Usando a função reflect.ValueOf(), podemos converter uma variável em reflect.Value. Exemplo de código:

go
func main() {
  str := "hello world!"
  reflectValue := reflect.ValueOf(str)
  fmt.Println(reflectValue)
}

Resultado:

hello world!

Type

go
func (v Value) Type() Type

O método Type pode obter o tipo de um valor de reflexão:

go
func main() {
   num := 114514
   rValue := reflect.ValueOf(num)
   fmt.Println(rValue.Type())
}

Output:

int

Elem

go
func (v Value) Elem() Value

Obtém o valor de reflexão do elemento de um valor de reflexão:

go
func main() {
   num := new(int)
   *num = 114514
   // Usando ponteiro como exemplo
   rValue := reflect.ValueOf(num).Elem()
   fmt.Println(rValue.Interface())
}

Output:

114514

Ponteiros

Há duas maneiras de obter o ponteiro de um valor de reflexão:

go
// Retorna um valor de reflexão de ponteiro representando o endereço de v
func (v Value) Addr() Value

// Retorna um uintptr apontando para o valor original de v, equivalente a uintptr(Value.Addr().UnsafePointer())
func (v Value) UnsafeAddr() uintptr

// Retorna um uintptr apontando para o valor original de v
// Apenas quando o Kind de v for Chan, Func, Map, Pointer, Slice, UnsafePointer, caso contrário panic
func (v Value) Pointer() uintptr

// Retorna um unsafe.Pointer apontando para o valor original de v
// Apenas quando o Kind de v for Chan, Func, Map, Pointer, Slice, UnsafePointer, caso contrário panic
func (v Value) UnsafePointer() unsafe.Pointer

Exemplo:

go
func main() {
   num := 1024
   ele := reflect.ValueOf(&num).Elem()
   fmt.Println("&num", &num)
   fmt.Println("Addr", ele.Addr())
   fmt.Println("UnsafeAddr", unsafe.Pointer(ele.UnsafeAddr()))
   fmt.Println("Pointer", unsafe.Pointer(ele.Addr().Pointer()))
   fmt.Println("UnsafePointer", ele.Addr().UnsafePointer())
}

Output:

&num 0xc0000a6058
Addr 0xc0000a6058
UnsafeAddr 0xc0000a6058
Pointer 0xc0000a6058
UnsafePointer 0xc0000a6058

TIP

fmt.Println refletirá o tipo do parâmetro. Se for do tipo reflect.Value, chamará automaticamente Value.Interface() para obter seu valor original.

Usando um map como exemplo:

go
func main() {
  dic := map[string]int{}
  ele := reflect.ValueOf(&dic).Elem()
  println(dic)
  fmt.Println("Addr", ele.Addr())
  fmt.Println("UnsafeAddr", *(*unsafe.Pointer)(unsafe.Pointer(ele.UnsafeAddr())))
  fmt.Println("Pointer", unsafe.Pointer(ele.Pointer()))
  fmt.Println("UnsafePointer", ele.UnsafePointer())
}

Output:

0xc00010e4b0
Addr &map[]
UnsafeAddr 0xc00010e4b0
Pointer 0xc00010e4b0
UnsafePointer 0xc00010e4b0

Definir Valor

go
func (v Value) Set(x Value)

Se quisermos modificar um valor de reflexão através de reflexão, seu valor deve ser endereçável. Neste caso, devemos modificar o valor do elemento através de um ponteiro, em vez de tentar modificar o valor do elemento diretamente.

go
func main() {
   // *int
   num := new(int)
   *num = 114514
   rValue := reflect.ValueOf(num)
    // Obtém o elemento apontado pelo ponteiro
   ele := rValue.Elem()
   fmt.Println(ele.Interface())
   ele.SetInt(11)
   fmt.Println(ele.Interface())
}

Output:

114514
11

Obter Valor

go
func (v Value) Interface() (i any)

Através do método Interface(), podemos obter o valor original do valor de reflexão:

go
func main() {
   var str string
   str = "hello"
   rValue := reflect.ValueOf(str)
   if v, ok := rValue.Interface().(string); ok {
      fmt.Println(v)
   }
}

Output:

hello

Função

Através de reflexão, podemos obter todas as informações de uma função e também chamar funções via reflexão.

Informações

Obter todas as informações de uma função através do tipo de reflexão:

go
func Max(a, b int) int {
   if a > b {
      return a
   }
   return b
}

func main() {
   rType := reflect.TypeOf(Max)
   // Output do nome da função, tipos de função literais não têm nome
   fmt.Println(rType.Name())
   // Output do número de parâmetros e valores de retorno
   fmt.Println(rType.NumIn(), rType.NumOut())
   rParamType := rType.In(0)
   // Output do tipo do primeiro parâmetro
   fmt.Println(rParamType.Kind())
   rResType := rType.Out(0)
   // Output do tipo do primeiro valor de retorno
   fmt.Println(rResType.Kind())
}

Output:


2 1
int
int

Chamada

Chamar uma função através de um valor de reflexão:

go
func (v Value) Call(in []Value) []Value
go
func main() {
   // Obtém o valor de reflexão da função
   rType := reflect.ValueOf(Max)
   // Passa o array de parâmetros
   rResValue := rType.Call([]reflect.Value{reflect.ValueOf(18), reflect.ValueOf(50)})
   for _, value := range rResValue {
      fmt.Println(value.Interface())
   }
}

Output:

50

Struct

Suponha que temos a seguinte struct:

go
type Person struct {
  Name    string `json:"name"`
  Age     int    `json:"age"`
  Address string `json:"address"`
  money   int
}

func (p Person) Talk(msg string) string {
  return msg
}

Acessar Campos

A estrutura de reflect.StructField é a seguinte:

go
type StructField struct {
  // Nome do campo
  Name string
  // Nome do pacote
  PkgPath string
  // Nome do tipo
  Type      Type
  // Tag
  Tag       StructTag
  // Offset em bytes do campo
  Offset    uintptr
  // Índice
  Index     []int
  // Se é um campo aninhado
  Anonymous bool
}

Há duas maneiras de acessar campos de uma struct: uma é através de índice, e a outra é através de nome.

go
type Type interface{
    Field(i int) StructField
}

Exemplo de acesso através de índice:

go
func main() {
  rType := reflect.TypeOf(new(Person)).Elem()
  // Output do número de campos da struct
  fmt.Println(rType.NumField())
  for i := 0; i < rType.NumField(); i++ {
    structField := rType.Field(i)
    fmt.Println(structField.Index, structField.Name, structField.Type, structField.Offset, structField.IsExported())
  }
}

Output:

4
[0] Name string 0 true
[1] Age int 16 true
[2] Address string 24 true
[3] money int 40 false
go
type Type interface{
    FieldByName(name string) (StructField, bool)
}

Exemplo de acesso através de nome:

go
func main() {
   rType := reflect.TypeOf(new(Person)).Elem()
   // Output do número de campos da struct
   fmt.Println(rType.NumField())
   if field, ok := rType.FieldByName("money"); ok {
      fmt.Println(field.Name, field.Type, field.IsExported())
   }
}

Output:

4
money int false

Modificar Campos

Se quisermos modificar o valor de um campo de struct, devemos passar um ponteiro para a struct. Aqui está um exemplo de modificação de campo:

go
func main() {
  // Passa ponteiro
  rValue := reflect.ValueOf(&Person{
    Name:    "",
    Age:     0,
    Address: "",
    money:   0,
  }).Elem()

  // Obtém o campo
  name := rValue.FieldByName("Name")
  // Modifica o valor do campo
  if (name != reflect.Value{}) { // Se retornar reflect.Value{}, significa que o campo não existe
    name.SetString("jack")
  }
  // Output da struct
  fmt.Println(rValue.Interface())
}

Output:

{jack 0  0}

Para modificar campos privados de uma struct, são necessárias algumas operações extras, como segue:

go
func main() {
  // Passa ponteiro
  rValue := reflect.ValueOf(&Person{
    Name:    "",
    Age:     0,
    Address: "",
    money:   0,
  }).Elem()

  // Obtém um campo privado
  money := rValue.FieldByName("money")
  // Modifica o valor do campo
  if (money != reflect.Value{}) {
    // Constrói um valor de reflexão de ponteiro apontando para o campo não exportado da struct
    p := reflect.NewAt(money.Type(), money.Addr().UnsafePointer())
    // Obtém o elemento apontado por este ponteiro, que é o campo a ser modificado
    field := p.Elem()
    // Modifica o valor
    field.SetInt(164)
  }
  // Output da struct
  fmt.Printf("%+v\n", rValue.Interface())
}

Acessar Tag

Após obter StructField, podemos acessar sua Tag diretamente:

go
// Se não existir, ok é false
func (tag StructTag) Lookup(key string) (value string, ok bool)

// Se não existir, retorna string vazia
func (tag StructTag) Get(key string) string

Exemplo:

go
func main() {
   rType := reflect.TypeOf(new(Person)).Elem()
   name, ok := rType.FieldByName("Name")
   if ok {
      fmt.Println(name.Tag.Lookup("json"))
      fmt.Println(name.Tag.Get("json"))
   }
}

Output:

name true
name

Acessar Métodos

Acessar métodos é muito semelhante ao processo de acessar campos, apenas as assinaturas das funções são ligeiramente diferentes. A struct reflect.Method é a seguinte:

go
type Method struct {
  // Nome do método
  Name string
  // Nome do pacote
  PkgPath string
  // Tipo do método
  Type  Type
  // Função correspondente ao método, o primeiro parâmetro é o receiver
  Func  Value
  // Índice
  Index int
}

Exemplo de acesso a informações de método:

go
func main() {
  // Obtém o tipo de reflexão da struct
  rType := reflect.TypeOf(new(Person)).Elem()
  // Output do número de métodos
  fmt.Println(rType.NumMethod())
  // Itera e output das informações dos métodos
  for i := 0; i < rType.NumMethod(); i++ {
    method := rType.Method(i)
    fmt.Println(method.Index, method.Name, method.Type, method.IsExported())
  }
}

Output:

1
0 Talk func(main.Person, string) string true

Se quisermos obter detalhes dos parâmetros e valores de retorno do método, podemos obtê-los através de Method.Func. O processo é o mesmo que acessar informações de função. Modificando ligeiramente o código acima:

go
func main() {
  // Obtém o tipo de reflexão da struct
  rType := reflect.TypeOf(new(Person)).Elem()
  // Output do número de métodos
  fmt.Println(rType.NumMethod())
  // Itera e output das informações dos métodos
  for i := 0; i < rType.NumMethod(); i++ {
    method := rType.Method(i)
    fmt.Println(method.Index, method.Name, method.Type, method.IsExported())
    fmt.Println("Parâmetros do método")
    for i := 0; i < method.Func.Type().NumIn(); i++ {
      fmt.Println(method.Func.Type().In(i).String())
    }
    fmt.Println("Valores de retorno do método")
    for i := 0; i < method.Func.Type().NumOut(); i++ {
      fmt.Println(method.Func.Type().Out(i).String())
    }
  }
}

Podemos ver que o primeiro parâmetro é main.Person, que é o tipo do receiver:

1
0 Talk func(main.Person, string) string true
Parâmetros do método
main.Person
string
Valores de retorno do método
string

Chamar Métodos

Chamar métodos é semelhante ao processo de chamar funções, e não é necessário passar o receiver manualmente. Exemplo:

go
func main() {
   // Obtém o tipo de reflexão da struct
   rValue := reflect.ValueOf(new(Person)).Elem()
   // Output do número de métodos
   fmt.Println(rValue.NumMethod())
   // Itera e output das informações dos métodos
   talk := rValue.MethodByName("Talk")
   if (talk != reflect.Value{}) {
      // Chama o método e obtém o valor de retorno
      res := talk.Call([]reflect.Value{reflect.ValueOf("hello,reflect!")})
      // Itera e output dos valores de retorno
      for _, re := range res {
         fmt.Println(re.Interface())
      }
   }
}

Output:

1
hello,reflect!

Criação

Através de reflexão, podemos construir novos valores. O pacote reflect também fornece funções mais convenientes para alguns tipos especiais.

Tipos Básicos

go
// Retorna um valor de reflexão de ponteiro apontando para o valor de reflexão
func New(typ Type) Value

Usando string como exemplo:

go
func main() {
   rValue := reflect.New(reflect.TypeOf(*new(string)))
   rValue.Elem().SetString("hello world!")
   fmt.Println(rValue.Elem().Interface())
}
hello world!

Struct

A criação de structs também usa a função reflect.New:

go
type Person struct {
   Name    string `json:"name"`
   Age     int    `json:"age"`
   Address string `json:"address"`
   money   int
}

func (p Person) Talk(msg string) string {
   return msg
}

func main() {
   // Cria valor de reflexão de struct
   rType := reflect.TypeOf(new(Person)).Elem()
   person := reflect.New(rType).Elem()
   fmt.Println(person.Interface())
}

Output:

{ 0  0}

Slice

Criar slice via reflexão:

go
func MakeSlice(typ Type, len, cap int) Value
go
func main() {
   // Cria valor de reflexão de slice
   rValue := reflect.MakeSlice(reflect.TypeOf(*new([]int)), 10, 10)
   // Itera e atribui valores
   for i := 0; i < 10; i++ {
      rValue.Index(i).SetInt(int64(i))
   }
   fmt.Println(rValue.Interface())
}
[0 1 2 3 4 5 6 7 8 9]

Map

Criar Map via reflexão:

go
func MakeMapWithSize(typ Type, n int) Value
go
func main() {
   // Constrói valor de reflexão de map
   rValue := reflect.MakeMapWithSize(reflect.TypeOf(*new(map[string]int)), 10)
   // Define valor
   rValue.SetMapIndex(reflect.ValueOf("a"), reflect.ValueOf(1))
   fmt.Println(rValue.Interface())
}
map[a:1]

Canal

Criar canal via reflexão:

go
func MakeChan(typ Type, buffer int) Value
go
func main() {
   // Cria valor de reflexão de canal
   makeChan := reflect.MakeChan(reflect.TypeOf(new(chan int)).Elem(), 0)
   fmt.Println(makeChan.Interface())
}

Função

Criar função via reflexão:

go
func MakeFunc(typ Type, fn func(args []Value) (results []Value)) Value
go
func main() {
    // Passa o tipo de wrapper e o corpo da função
  fn := reflect.MakeFunc(reflect.TypeOf(new(func(int))).Elem(), func(args []reflect.Value) (results []reflect.Value) {
    for _, arg := range args {
      fmt.Println(arg.Interface())
    }
    return nil
  })
  fmt.Println(fn.Type())
  fn.Call([]reflect.Value{reflect.ValueOf(1024)})
}

Output:

func(int)
1024

Igualdade Completa

reflect.DeepEqual é uma função fornecida pelo pacote de reflexão para determinar se duas variáveis são completamente iguais. A assinatura é a seguinte:

go
func DeepEqual(x, y any) bool

Esta função lida com cada tipo básico. Aqui estão algumas formas de julgamento de tipo:

  • Array: Cada elemento no array é completamente igual
  • Slice: Ambos são nil, julgados como completamente iguais, ou ambos não estão vazios e os elementos dentro do comprimento são completamente iguais
  • Struct: Todos os campos são completamente iguais
  • Map: Ambos são nil, são completamente iguais. Se ambos não são nil, cada valor mapeado por cada chave é completamente igual
  • Ponteiro: Apontam para o mesmo elemento ou os elementos apontados são completamente iguais
  • Interface: Quando os tipos concretos das interfaces são completamente iguais
  • Função: Apenas quando ambos são nil são completamente iguais, caso contrário não são completamente iguais

Aqui estão alguns exemplos:

Slice

go
func main() {
   a := make([]int, 100)
   b := make([]int, 100)
   fmt.Println(reflect.DeepEqual(a, b))
}

Output:

true

Struct

go
func main() {
   mike := Person{
      Name:   "mike",
      Age:    39,
      Father: nil,
   }

   jack := Person{
      Name:   "jack",
      Age:    18,
      Father: &mike,
   }

   tom := Person{
      Name:   "tom",
      Age:    18,
      Father: &mike,
   }
   fmt.Println(reflect.DeepEqual(mike, jack))
   fmt.Println(reflect.DeepEqual(tom, jack))
   fmt.Println(reflect.DeepEqual(jack, jack))
}

Output:

false
false
true

Golang por www.golangdev.cn edit