Introdução: Por que Go e por que é tão difícil?
\n\nGo (ou Golang) é uma linguagem que é amada pela simplicidade e odiada pela mesma simplicidade. Em entrevistas de Go, raramente perguntam "escreva uma ordenação bolha" (embora isso também aconteça). Na maioria das vezes, você encontrará problemas de concorrência, compreensão do gerenciamento de memória e capacidade de escrever código limpo e idiomático. Se você está acostumado com Python ou Java, Go pode parecer muito rigoroso. Mas é exatamente esse rigor que o torna ideal para sistemas de produção.
\n\nNeste artigo, abordaremos os tópicos principais que certamente surgirão na entrevista. Você não terá apenas teoria, mas exemplos concretos de código e explicações "por baixo dos panos". Vamos lá!
\n\n1. Goroutines e canais: o básico que você precisa saber de cor
\n\nGoroutines não são threads
\nA pergunta mais comum: "Qual a diferença entre uma goroutine e uma thread do SO?". Resposta: uma goroutine é leve (pilha a partir de 2 KB), gerenciada pelo escalonador do Go, não pelo kernel. Um milhão de goroutines é normal. Um milhão de threads mataria o sistema.
\n\n// Exemplo: iniciar 10 goroutines\nfor i := 0; i < 10; i++ {\n go func(id int) {\n fmt.Printf("Goroutine %d funcionando\\n", id)\n }(i)\n}\n// Aguardar (má maneira, apenas para exemplo)\ntime.Sleep(time.Second)\n\n\nCanais: sincronização sem dor
\nCanais em Go são "tubos" tipados para transferir dados entre goroutines. Lembre-se de três regras:
\n- \n
- Escrever em um canal fechado causa pânico. \n
- Ler de um canal fechado retorna o valor zero. \n
- Um canal sem buffer bloqueia a goroutine até que um receptor apareça. \n
// Trabalhando corretamente com canais\nch := make(chan int, 2) // com buffer\n\nch <- 1\nch <- 2\nclose(ch)\n\nfor val := range ch {\n fmt.Println(val) // 1, 2\n}\n\n\nDica para a entrevista: se perguntarem sobre "select", mostre um exemplo com timeout:
\n\nselect {\ncase msg := <-ch:\n fmt.Println(msg)\ncase <-time.After(1 * time.Second):\n fmt.Println("Timeout!")\n}\n\n\n2. Interfaces: não apenas métodos, mas contratos
\n\nEm Go, as interfaces são implementadas implicitamente. Isso significa que uma struct automaticamente satisfaz uma interface se tiver todos os métodos necessários. Frequentemente perguntam: "Qual a diferença entre interface{} e any?" — resposta: nenhuma, são sinônimos (desde Go 1.18).
Problema típico de entrevista
\nEscreva uma função que recebe um io.Writer e escreve uma string nele. Isso testa a compreensão das interfaces da biblioteca padrão.
func WriteHello(w io.Writer) {\n _, err := w.Write([]byte("Hello, Go!"))\n if err != nil {\n log.Fatal(err)\n }\n}\n\n// Uso:\nvar buf bytes.Buffer\nWriteHello(&buf)\nfmt.Println(buf.String()) // Hello, Go!\n\n\nArmadilha: todas as interfaces em Go são dois ponteiros (tipo + valor). Uma interface vazia (interface{}) não é igual a nil se contiver (*int)(nil). Lembre-se disso para não falhar na pergunta sobre nil != nil.
3. Genéricos: novo, mas já obrigatório
\n\nDesde Go 1.18, os genéricos fazem parte da linguagem. Na entrevista, podem pedir para escrever uma função genérica. Por exemplo, uma função que recebe um slice de qualquer tipo e retorna seu comprimento (trivial) ou encontra o máximo.
\n\n// Função genérica para encontrar o máximo\nfunc Max[T constraints.Ordered](a, b T) T {\n if a > b {\n return a\n }\n return b\n}\n\n// Uso:\nfmt.Println(Max(10, 20)) // 20\nfmt.Println(Max(3.14, 2.71)) // 3.14\n\n\nObserve o pacote constraints — ele é frequentemente usado em perguntas. Mas lembre-se: desde Go 1.21, ele foi substituído pelo cmp.Ordered embutido.
4. Tratamento de erros: não os ignore
\n\nEm Go, não há try-catch. Erros são valores.