Entrevista avançada de Go: de goroutines a genéricos
\n\nA entrevista para uma posição de desenvolvedor Go de nível médio ou sênior não é apenas uma verificação de sintaxe. Os empregadores esperam um profundo entendimento do modelo de concorrência, funcionamento do runtime, gerenciamento de memória e particularidades da tipagem. Neste artigo, analisaremos as principais perguntas avançadas sobre Golang que ajudarão você a passar com confiança em uma entrevista técnica. Não vamos nos ater a conceitos básicos como "o que é uma slice" — vamos direto para tópicos complexos com exemplos de código.
\n\n1. Goroutines, canais e padrões de sincronização
\n\nPerguntas sobre goroutines e canais são clássicas em qualquer entrevista. No entanto, em nível avançado, perguntarão não apenas "o que é uma goroutine", mas também sobre os detalhes do funcionamento do escalonador (modelo GMP) e o uso correto de primitivas de sincronização.
\n\nPergunta: Como funciona o escalonador de goroutines em Go? O que são G, M, P?
\nO escalonador Go usa o modelo GMP: G (goroutine) — thread leve, M (machine) — thread do SO, P (processor) — contexto de execução. P está vinculado a um processador lógico e gerencia a fila de goroutines. Quando uma goroutine é bloqueada (por exemplo, em uma chamada de sistema), P pode alternar M para outra goroutine, garantindo multitarefa eficiente sem criar milhares de threads do SO.
Pergunta: Qual é a diferença entre um canal bufferizado e não bufferizado? Dê um exemplo de deadlock.
\n\n// Canal não bufferizado — bloqueia o remetente até o recebimento\nch := make(chan int)\ngo func() {\n ch <- 42 // bloqueia até main ler\n}()\nfmt.Println(<-ch) // 42\n\n// Canal bufferizado — não bloqueia enquanto o buffer não estiver cheio\nchBuf := make(chan int, 2)\nchBuf <- 1\nchBuf <- 2 // ok, buffer não está cheio\n// chBuf <- 3 // deadlock, se não houver leitor\n\n// Deadlock típico: leitura de um canal não bufferizado vazio\nchDead := make(chan int)\n// <-chDead // fatal error: all goroutines are asleep — deadlock!\n\n\nPergunta: Como fechar um canal com segurança? O que é "range over channel"?
\nApenas o remetente deve fechar o canal. O receptor pode usar o segundo parâmetro (v, ok := <-ch) para verificar. A iteração for v := range ch termina quando o canal é fechado. Tentar escrever em um canal fechado causa pânico.
ch := make(chan int)\ngo func() {\n for i := 0; i < 3; i++ {\n ch <- i\n }\n close(ch) // obrigatório fechar\n}()\nfor v := range ch {\n fmt.Println(v) // 0, 1, 2\n}\n\n\n2. Interfaces, interface vazia e type assertions
\n\nInterfaces em Go são contratos. Na entrevista, é importante entender como elas são estruturadas internamente (estrutura iface e eface) e a diferença entre tipagem estática e dinâmica.
Pergunta: O que é uma interface vazia (interface{}) e quando usá-la?
A interface vazia não contém métodos, portanto qualquer tipo a implementa. É usada para trabalhar com dados de tipo desconhecido (por exemplo, json.Unmarshal). No entanto, o uso excessivo da interface vazia viola a tipagem forte do Go e pode levar a erros em tempo de execução.
Pergunta: Como funciona o type assertion? Qual a diferença do type switch?
\n\nvar val interface{} = "hello"\n\n// type assertion\nstr, ok := val.(string)\nif ok {\n fmt.Println("É uma string:", str)\n}\n\n// type switch\nswitch v := val.(type) {\ncase string:\n fmt.Println("String:", v)\ncase int:\n fmt.Println("Número:", v)\ndefault:\n fmt.Println("Tipo desconhecido")\n}\n\n\nPergunta: Como as interfaces são armazenadas na memória?
\nCada interface é composta por dois ponteiros: um para o tipo (itable) e outro para o valor (data). Para a interface vazia (interface{}), c