Desafio Golang: escreva uma funçãopercorre(x interface{}, fn func(string))
que recebe uma structx
e chamafn
para todos os campos string encontrados dentro dela. nível de dificuldade: recursão.
reflection
(reflexão).A reflexão em computação é a habilidade de um programa examinar sua própria estrutura, particularmente através de tipos; é uma forma de metaprogramação. Também é uma ótima fonte de confusão.
interface
?string
, int
e nossos próprios tipos como ContaBancaria
.interface{}
, que você pode relacionar com qualquer tipo.percorre(x interface{}, fn func(string))
aceitará qualquer valor para x
.interface
para tudo e ter funções bem flexíveis?interface
, você perde a segurança de tipos. E se você quisesse passar Foo.bar
do tipo string
para uma função, mas ao invés disso passa Foo.baz
do tipo int
? O compilador não vai ser capaz de informar seu erro. Você também não tem ideia do que pode passar para uma função. Saber que uma função recebe um ServicoDeUsuario
, por exemplo, é muito útil.interface{}
, só para esclarecer) para que os usuários possam usar sua função com vários tipos se implementarem os métodos que você precisar para a sua função funcionar.x
). Depois, podemos espiar a função (fn
) passada para ela para ver se ela foi chamada.resultado
) que armazena quais strings foram passadas dentro de fn
pelo percorre
. Algumas vezes, nos capítulos anteriores, criamos tipos dedicados para isso para espionar chamadas de função/método, mas nesse caso vamos apenas passá-lo em uma função anônima para fn
que acaba em resultado
.struct
anônima com um campo Nome
do tipo string para partir para caminho "feliz" e mais simples.percorre
com x
e o espião e por enquanto só verificamos o tamanho de resultado
. Teremos mais precisão nas nossas verificações quando tivermos algo bem básico funcionando.percorre
.fn
.fn
está correta:x
.ValueOf
que retorna um Value
(valor) de determinada variável. Isso nos permite inspecionar um valor, inclusive seus campos usados nas próximas linhas.String()
que tetorna o valor subjacente como string, mas sabemos que vai dar errado se o campo for de algum tipo que não uma string.fn
foi chamado.casos
.valor
tem um método chamado NumField
que retorna a quantidade de campos no valor. Isso nos permite iterar sobre os campos e chamar fn
, o que faz nosso teste passar.percorre
é que ela presume que todo campo é uma string
. Vamos escrever um teste para esse caso.string
.struct
"única"? Em outras palavras, o que acontece se tivermos uma struct
com alguns campos aninhados?percorre
novamente na nossa estrutura de dentro.switch
vai melhorar a legibilidade e tornar seu código mais fácil de estender.NumField
em um ponteiro Value
e precisamos extrair o valor antes disso usando Elem()
.reflect.Value
de determinada interface{}
para uma função.reflect.Value
de x
para que eu possa inspecioná-lo, não me importa de qual forma.NumField
em nosso reflect.Value
, mas não há um por não ser uma struct.percorre
em:return
para parar a execução do restante do código) e se não for, só vamos presumir que é uma estrutura.percorre
para cada um. Por outro lado, se for um reflect.String
, podemos apenas chamar fn
.percorre
sendo que conceitualmente são a mesma coisa.valor
for um reflect.String
, chamamos fn
normalmente.switch
vai extrair duas coisas dependendo do tipo:Value
(Field
[campo] ou Index
[índice])quantidadeDeValores
chamando percorre
com o resultado da função getField
.map
.map
é bem parecido com a struct
, mas as chaves são desconhecidas em tempo de compilação.map
is very similar to struct
, it's just the keys are unknown at compile time.percorreValor
, que encapsula chamadas para percorre
dentro do nosso switch
para que só tenham que extrair os reflect.Value
de valor
.fn
em uma ordem específica.verificaSeContem
:reflect
.