Preguntas de entrevista de Go/Golang con código y respuestas (40+)
Go es uno de los lenguajes que más tracción tiene en el mercado remoto. Lo usan Google, Uber, Cloudflare, Twitch, Docker — y el pool de candidatos que realmente lo domina sigue siendo pequeño. Esa es tu ventaja.
Esta guía cubre las 40+ preguntas que más caen en entrevistas técnicas de Go, desde conceptos básicos hasta concurrencia avanzada. Cada respuesta tiene código que podés copiar, entender y adaptar.
Preguntas básicas de Go
1. ¿Qué es Go y cuáles son sus características principales?
Go (también llamado Golang) es un lenguaje compilado, tipado estáticamente, creado por Google en 2009. Sus características principales:
- Compilación rápida — un proyecto grande compila en segundos
- Garbage collection — manejo automático de memoria
- Concurrencia nativa — goroutines y channels integrados al lenguaje
- Tipado estático con inferencia de tipos
- Sin herencia — composición en vez de herencia
- Binario único — se compila en un solo ejecutable sin dependencias
package main
import "fmt"
func main() {
fmt.Println("Hello, World!")
}2. ¿Cuál es la diferencia entre `var` y `:=`?
var es la declaración explícita de variable, funciona dentro y fuera de funciones. := es la declaración corta con inferencia de tipo, solo funciona dentro de funciones.
package main
import "fmt"
var globalVar = "soy global" // var funciona a nivel paquete
func main() {
var x int = 10 // declaración explícita
var y = 20 // Go infiere el tipo (int)
z := 30 // declaración corta, equivalente a var z int = 30
fmt.Println(x, y, z)
fmt.Println(globalVar)
}En la entrevista: explicá que := solo vive dentro de funciones y que var se usa cuando necesitás declarar sin inicializar o cuando querés ser explícito sobre el tipo.
3. ¿Cómo funcionan los tipos de datos en Go?
Go tiene tipos básicos, tipos compuestos y tipos de referencia.
package main
import "fmt"
func main() {
// Tipos básicos
var i int = 42
var f float64 = 3.14
var b bool = true
var s string = "golang"
// Tipos compuestos
arr := [3]int{1, 2, 3} // array — tamaño fijo
slice := []int{4, 5, 6} // slice — tamaño dinámico
m := map[string]int{"a": 1} // map
// Tipo de referencia: pointer
ptr := &i
fmt.Println(*ptr) // 42
fmt.Println(i, f, b, s, arr, slice, m)
}4. ¿Qué es un slice y cómo difiere de un array?
Un array tiene tamaño fijo definido en tiempo de compilación. Un slice es una vista dinámica sobre un array subyacente.
package main
import "fmt"
func main() {
// Array — tamaño fijo, parte del tipo
arr := [5]int{1, 2, 3, 4, 5}
fmt.Println(len(arr)) // 5
// Slice — dinámico, tres componentes: puntero, longitud, capacidad
s := []int{1, 2, 3}
s = append(s, 4, 5)
fmt.Println(len(s), cap(s)) // 5 6 (cap puede crecer)
// Slice de un array
sub := arr[1:3]
fmt.Println(sub) // [2 3]
// make — slice con longitud y capacidad explícitas
s2 := make([]int, 3, 10)
fmt.Println(len(s2), cap(s2)) // 3 10
}Dato importante: cuando dos slices comparten el mismo array subyacente, modificar uno modifica al otro.
a := []int{1, 2, 3, 4, 5}
b := a[1:3] // b apunta al mismo array
b[0] = 99
fmt.Println(a) // [1 99 3 4 5] — a cambió también5. ¿Cómo funcionan los maps en Go?
package main
import "fmt"
func main() {
m := map[string]int{
"alice": 30,
"bob": 25,
}
age := m["alice"]
fmt.Println(age) // 30
val, ok := m["charlie"]
if !ok {
fmt.Println("charlie no existe, val es:", val) // val es 0 (zero value)
}
m["charlie"] = 28
delete(m, "bob")
for key, value := range m {
fmt.Printf("%s: %d\n", key, value)
}
}En la entrevista: los maps no son seguros para uso concurrente. Para eso existe sync.Map.
6. ¿Qué es el zero value en Go?
Go inicializa todas las variables a su "zero value" automáticamente.
package main
import "fmt"
func main() {
var i int // 0
var f float64 // 0.0
var b bool // false
var s string // ""
var p *int // nil
var sl []int // nil (pero len(sl) == 0)
var m map[string]int // nil
fmt.Println(i, f, b, s, p, sl, m)
}7. ¿Cómo funciona el manejo de errores en Go?
Go no tiene excepciones. Los errores son valores que se retornan como último valor de retorno.
package main
import (
"errors"
"fmt"
)
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("no se puede dividir por cero")
}
return a / b, nil
}
type ValidationError struct {
Field string
Message string
}
func (e *ValidationError) Error() string {
return fmt.Sprintf("validación fallida en %s: %s", e.Field, e.Message)
}
func validateAge(age int) error {
if age < 0 {
return &ValidationError{Field: "age", Message: "no puede ser negativo"}
}
return nil
}
func main() {
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println(result) // 5
_, err = divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
}
err = validateAge(-5)
var valErr *ValidationError
if errors.As(err, &valErr) {
fmt.Println("Campo con error:", valErr.Field)
}
}8. ¿Qué son las funciones `defer`, `panic` y `recover`?
package main
import "fmt"
func riskyOperation() {
defer func() {
if r := recover(); r != nil {
fmt.Println("recuperado del panic:", r)
}
}()
panic("algo salió muy mal")
}
func readFile() {
fmt.Println("abriendo archivo")
defer fmt.Println("cerrando archivo")
fmt.Println("leyendo contenido")
}
func main() {
readFile()
fmt.Println("---")
riskyOperation()
fmt.Println("el programa continúa después del recover")
}En la entrevista: defer es para cleanup. panic es para errores irrecuperables. recover solo funciona dentro de una función defer.
Structs, interfaces y tipos
9. ¿Cómo funcionan los structs en Go?
package main
import "fmt"
type Person struct {
Name string
Age int
}
func (p Person) Greet() string {
return fmt.Sprintf("Hola, soy %s", p.Name)
}
func (p *Person) Birthday() {
p.Age++
}
func main() {
p2 := Person{
Name: "Bob",
Age: 25,
}
type Employee struct {
Person
Company string
Salary float64
}
emp := Employee{
Person: Person{Name: "Charlie", Age: 35},
Company: "Acme",
Salary: 75000,
}
p2.Birthday()
fmt.Println(p2.Age) // 26
fmt.Println(emp.Name) // acceso directo via embedding
fmt.Println(emp.Greet()) // método de Person disponible en Employee
}10. ¿Cómo funcionan las interfaces en Go?
Las interfaces en Go son implícitas: un tipo implementa una interfaz automáticamente si tiene todos sus métodos.
package main
import (
"fmt"
"math"
)
type Shape interface {
Area() float64
Perimeter() float64
}
type Circle struct {
Radius float64
}
func (c Circle) Area() float64 {
return math.Pi * c.Radius * c.Radius
}
func (c Circle) Perimeter() float64 {
return 2 * math.Pi * c.Radius
}
type Rectangle struct {
Width, Height float64
}
func (r Rectangle) Area() float64 {
return r.Width * r.Height
}
func (r Rectangle) Perimeter() float64 {
return 2 * (r.Width + r.Height)
}
func printShapeInfo(s Shape) {
fmt.Printf("Área: %.2f, Perímetro: %.2f\n", s.Area(), s.Perimeter())
}
func main() {
c := Circle{Radius: 5}
r := Rectangle{Width: 4, Height: 6}
printShapeInfo(c)
printShapeInfo(r)
var anything interface{} = c
if circle, ok := anything.(Circle); ok {
fmt.Println("Es un círculo con radio:", circle.Radius)
}
switch v := anything.(type) {
case Circle:
fmt.Println("Circle:", v.Radius)
case string:
fmt.Println("String:", v)
default:
fmt.Printf("Tipo desconocido: %T\n", v)
}
}11. ¿Cuál es la diferencia entre value receiver y pointer receiver?
package main
import "fmt"
type Counter struct {
count int
}
func (c Counter) Get() int {
return c.count
}
func (c *Counter) Increment() {
c.count++
}
func main() {
c := Counter{}
c.Increment()
c.Increment()
fmt.Println(c.Get()) // 2
}Regla: usá pointer receiver si el método modifica el struct, si el struct es grande, o para consistencia con otros métodos del mismo tipo.
12. ¿Qué es la interfaz `error` y cómo usar `fmt.Errorf` con `%w`?
package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("not found")
type DBError struct {
Code int
Message string
Err error
}
func (e *DBError) Error() string {
return fmt.Sprintf("db error %d: %s", e.Code, e.Message)
}
func (e *DBError) Unwrap() error {
return e.Err
}
func fetchUser(id int) error {
if id == 0 {
return &DBError{Code: 404, Message: "usuario no encontrado", Err: ErrNotFound}
}
return nil
}
func getUser(id int) error {
err := fetchUser(id)
if err != nil {
return fmt.Errorf("getUser(%d): %w", id, err)
}
return nil
}
func main() {
err := getUser(0)
if err != nil {
fmt.Println(err)
if errors.Is(err, ErrNotFound) {
fmt.Println("el recurso no existe")
}
var dbErr *DBError
if errors.As(err, &dbErr) {
fmt.Println("código de error:", dbErr.Code)
}
}
}Concurrencia
13. ¿Qué es una goroutine?
Una goroutine es una función que se ejecuta concurrentemente con el resto del programa. Es liviana (2KB de stack inicial) y el runtime de Go puede manejar millones de ellas.
package main
import (
"fmt"
"sync"
)
func worker(id int, wg *sync.WaitGroup) {
defer wg.Done()
fmt.Printf("Worker %d iniciando\n", id)
fmt.Printf("Worker %d terminado\n", id)
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go worker(i, &wg)
}
wg.Wait()
fmt.Println("Todas las goroutines terminaron")
}14. ¿Qué son los channels y cómo funcionan?
package main
import "fmt"
func sum(s []int, ch chan int) {
total := 0
for _, v := range s {
total += v
}
ch <- total
}
func main() {
s := []int{7, 2, 8, -9, 4, 0}
ch := make(chan int)
go sum(s[:len(s)/2], ch)
go sum(s[len(s)/2:], ch)
x, y := <-ch, <-ch
fmt.Println(x, y, x+y)
}15. ¿Cuál es la diferencia entre channels con y sin buffer?
package main
import "fmt"
func main() {
// Sin buffer — síncrono: el envío bloquea hasta que alguien recibe
unbuffered := make(chan int)
go func() {
unbuffered <- 42
}()
fmt.Println(<-unbuffered)
// Con buffer — asíncrono hasta llenar el buffer
buffered := make(chan int, 3)
buffered <- 1
buffered <- 2
buffered <- 3
fmt.Println(<-buffered) // 1
fmt.Println(<-buffered) // 2
fmt.Println(<-buffered) // 3
}16. ¿Cómo usar `select` con channels?
package main
import (
"fmt"
"time"
)
func main() {
ch1 := make(chan string)
ch2 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
ch1 <- "uno"
}()
go func() {
time.Sleep(2 * time.Second)
ch2 <- "dos"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-ch1:
fmt.Println("Recibido de ch1:", msg1)
case msg2 := <-ch2:
fmt.Println("Recibido de ch2:", msg2)
case <-time.After(3 * time.Second):
fmt.Println("timeout")
}
}
}17. ¿Qué es un mutex y cuándo usarlo?
package main
import (
"fmt"
"sync"
)
type SafeCounter struct {
mu sync.Mutex
v map[string]int
}
func (c *SafeCounter) Inc(key string) {
c.mu.Lock()
defer c.mu.Unlock()
c.v[key]++
}
func (c *SafeCounter) Value(key string) int {
c.mu.RLock()
defer c.mu.RUnlock()
return c.v[key]
}
func main() {
c := SafeCounter{v: make(map[string]int)}
var wg sync.WaitGroup
for i := 0; i < 1000; i++ {
wg.Add(1)
go func() {
defer wg.Done()
c.Inc("key")
}()
}
wg.Wait()
fmt.Println(c.Value("key")) // 1000
}18. ¿Qué es un race condition y cómo detectarlo?
Detectarlo con el race detector:
go run -race main.go
go test -race ./...Solución con atomic:
import "sync/atomic"
var counter int64
atomic.AddInt64(&counter, 1)19. ¿Qué es el patrón worker pool?
package main
import (
"fmt"
"sync"
)
func workerPool(numWorkers int, jobs <-chan int, results chan<- int, wg *sync.WaitGroup) {
for w := 0; w < numWorkers; w++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
for job := range jobs {
results <- job * job
}
}(w)
}
}
func main() {
const numJobs = 9
const numWorkers = 3
jobs := make(chan int, numJobs)
results := make(chan int, numJobs)
var wg sync.WaitGroup
workerPool(numWorkers, jobs, results, &wg)
for j := 1; j <= numJobs; j++ {
jobs <- j
}
close(jobs)
go func() {
wg.Wait()
close(results)
}()
total := 0
for r := range results {
total += r
}
fmt.Println("Total:", total)
}Context y cancelación
20. ¿Cómo usar `context.Context`?
package main
import (
"context"
"fmt"
"time"
)
func doWork(ctx context.Context, name string) error {
select {
case <-time.After(2 * time.Second):
fmt.Printf("%s: completado\n", name)
return nil
case <-ctx.Done():
fmt.Printf("%s: cancelado — %v\n", name, ctx.Err())
return ctx.Err()
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
err := doWork(ctx, "operación-1")
if err != nil {
fmt.Println("Error:", err) // context deadline exceeded
}
ctx2, cancel2 := context.WithCancel(context.Background())
go func() {
time.Sleep(500 * time.Millisecond)
cancel2()
}()
err = doWork(ctx2, "operación-2")
if err != nil {
fmt.Println("Error:", err) // context canceled
}
}Generics (Go 1.18+)
21. ¿Qué son los generics en Go?
package main
import "fmt"
func Contains[T comparable](slice []T, item T) bool {
for _, v := range slice {
if v == item {
return true
}
}
return false
}
func Map[T, U any](slice []T, f func(T) U) []U {
result := make([]U, len(slice))
for i, v := range slice {
result[i] = f(v)
}
return result
}
type Stack[T any] struct {
items []T
}
func (s *Stack[T]) Push(item T) {
s.items = append(s.items, item)
}
func (s *Stack[T]) Pop() (T, bool) {
if len(s.items) == 0 {
var zero T
return zero, false
}
item := s.items[len(s.items)-1]
s.items = s.items[:len(s.items)-1]
return item, true
}
func main() {
ints := []int{1, 2, 3, 4, 5}
fmt.Println(Contains(ints, 3)) // true
doubled := Map(ints, func(x int) int { return x * 2 })
fmt.Println(doubled) // [2 4 6 8 10]
s := Stack[string]{}
s.Push("a")
s.Push("b")
val, _ := s.Pop()
fmt.Println(val) // b
}Testing
22. ¿Cómo escribir tests en Go?
// calculator_test.go
package calculator
import "testing"
func TestAddTableDriven(t *testing.T) {
tests := []struct {
name string
a, b int
expected int
}{
{"positivos", 2, 3, 5},
{"negativos", -2, -3, -5},
{"mixtos", -2, 3, 1},
{"ceros", 0, 0, 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := Add(tt.a, tt.b)
if result != tt.expected {
t.Errorf("Add(%d, %d) = %d; want %d", tt.a, tt.b, result, tt.expected)
}
})
}
}go test ./...
go test -v ./...
go test -race ./...
go test -bench=. -benchmem ./...HTTP y APIs
23. ¿Cómo construir una API REST básica en Go?
package main
import (
"encoding/json"
"log"
"net/http"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
}
var users = []User{
{ID: 1, Name: "Alice", Age: 30},
{ID: 2, Name: "Bob", Age: 25},
}
func usersHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
switch r.Method {
case http.MethodGet:
json.NewEncoder(w).Encode(users)
case http.MethodPost:
var newUser User
if err := json.NewDecoder(r.Body).Decode(&newUser); err != nil {
http.Error(w, "JSON inválido", http.StatusBadRequest)
return
}
newUser.ID = len(users) + 1
users = append(users, newUser)
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(newUser)
default:
http.Error(w, "Método no permitido", http.StatusMethodNotAllowed)
}
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/users", usersHandler)
log.Fatal(http.ListenAndServe(":8080", mux))
}24. ¿Cómo implementar middleware en Go?
package main
import (
"log"
"net/http"
"time"
)
func Logger(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
})
}
func RequireAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token != "Bearer secret-token" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
next.ServeHTTP(w, r)
})
}
func main() {
hello := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("Hello!"))
})
protected := Logger(RequireAuth(hello))
http.Handle("/hello", protected)
log.Fatal(http.ListenAndServe(":8080", nil))
}Patrones avanzados
25. ¿Qué es el patrón functional options?
package main
import (
"fmt"
"time"
)
type Server struct {
host string
port int
timeout time.Duration
maxConn int
}
type Option func(*Server)
func WithHost(host string) Option {
return func(s *Server) { s.host = host }
}
func WithPort(port int) Option {
return func(s *Server) { s.port = port }
}
func WithTimeout(timeout time.Duration) Option {
return func(s *Server) { s.timeout = timeout }
}
func NewServer(opts ...Option) *Server {
s := &Server{
host: "localhost",
port: 8080,
timeout: 30 * time.Second,
maxConn: 100,
}
for _, opt := range opts {
opt(s)
}
return s
}
func main() {
s := NewServer(
WithHost("0.0.0.0"),
WithPort(9090),
WithTimeout(60*time.Second),
)
fmt.Printf("%+v\n", s)
}26. ¿Qué es el patrón singleton en Go?
package main
import (
"fmt"
"sync"
)
type Database struct {
connectionString string
}
var (
instance *Database
once sync.Once
)
func GetDatabase() *Database {
once.Do(func() {
instance = &Database{connectionString: "host=localhost port=5432"}
fmt.Println("Database inicializada")
})
return instance
}27. ¿Cómo implementar el patrón pipeline con channels?
package main
import "fmt"
func generate(nums ...int) <-chan int {
out := make(chan int)
go func() {
for _, n := range nums {
out <- n
}
close(out)
}()
return out
}
func square(in <-chan int) <-chan int {
out := make(chan int)
go func() {
for n := range in {
out <- n * n
}
close(out)
}()
return out
}
func main() {
c := generate(1, 2, 3, 4, 5)
sq := square(c)
for n := range sq {
fmt.Println(n) // 1, 4, 9, 16, 25
}
}Preguntas trampa que caen seguido
28. ¿Qué imprime este código?
package main
import "fmt"
func main() {
x := 5
defer fmt.Println(x) // argumento evaluado AHORA
x = 10
fmt.Println(x)
}
// Output:
// 10
// 5El argumento de defer se evalúa cuando se llama defer, no cuando se ejecuta.
29. ¿Qué es el "nil interface" gotcha?
package main
import "fmt"
type MyError struct{ msg string }
func (e *MyError) Error() string { return e.msg }
// MAL — retorna interface con tipo no-nil pero valor nil
func badFunction(fail bool) error {
var err *MyError = nil
if fail {
err = &MyError{"algo falló"}
}
return err // (*MyError)(nil) no es nil como interface
}
// BIEN
func goodFunction(fail bool) error {
if fail {
return &MyError{"algo falló"}
}
return nil
}
func main() {
err := badFunction(false)
if err != nil {
fmt.Println("error:", err) // se imprime aunque "no hay error"
}
err2 := goodFunction(false)
if err2 != nil {
fmt.Println("error:", err2) // no se imprime, correcto
}
}Una interface es nil solo cuando tipo y valor son nil.
30. ¿Qué es un `goroutine leak` y cómo evitarlo?
package main
import (
"context"
"fmt"
"time"
)
// BIEN — usar context para cancelación
func safeOperation(ctx context.Context) <-chan int {
ch := make(chan int, 1)
go func() {
select {
case <-time.After(10 * time.Second):
ch <- 42
case <-ctx.Done():
fmt.Println("goroutine cancelada limpiamente")
return
}
}()
return ch
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
ch := safeOperation(ctx)
select {
case result := <-ch:
fmt.Println("resultado:", result)
case <-ctx.Done():
fmt.Println("timeout:", ctx.Err())
}
}31. ¿Cómo funciona `copy()` con slices?
package main
import "fmt"
func main() {
src := []int{1, 2, 3, 4, 5}
dst := make([]int, len(src))
copy(dst, src)
dst[0] = 99
fmt.Println(src) // [1 2 3 4 5] — sin cambios
fmt.Println(dst) // [99 2 3 4 5]
}32. ¿Cómo implementarías una caché LRU thread-safe?
package main
import (
"container/list"
"fmt"
"sync"
)
type LRUCache struct {
capacity int
mu sync.Mutex
list *list.List
items map[string]*list.Element
}
type entry struct {
key string
value interface{}
}
func NewLRUCache(capacity int) *LRUCache {
return &LRUCache{
capacity: capacity,
list: list.New(),
items: make(map[string]*list.Element),
}
}
func (c *LRUCache) Get(key string) (interface{}, bool) {
c.mu.Lock()
defer c.mu.Unlock()
if elem, ok := c.items[key]; ok {
c.list.MoveToFront(elem)
return elem.Value.(*entry).value, true
}
return nil, false
}
func (c *LRUCache) Put(key string, value interface{}) {
c.mu.Lock()
defer c.mu.Unlock()
if elem, ok := c.items[key]; ok {
c.list.MoveToFront(elem)
elem.Value.(*entry).value = value
return
}
if c.list.Len() == c.capacity {
oldest := c.list.Back()
if oldest != nil {
c.list.Remove(oldest)
delete(c.items, oldest.Value.(*entry).key)
}
}
e := &entry{key, value}
elem := c.list.PushFront(e)
c.items[key] = elem
}
func main() {
cache := NewLRUCache(3)
cache.Put("a", 1)
cache.Put("b", 2)
cache.Put("c", 3)
v, _ := cache.Get("a")
fmt.Println("a:", v)
cache.Put("d", 4) // evicts "b"
_, ok := cache.Get("b")
fmt.Println("b existe:", ok) // false
}33. ¿Qué son las init functions?
package main
import "fmt"
var config map[string]string
func init() {
config = make(map[string]string)
config["env"] = "production"
}
func main() {
fmt.Println(config)
}Se usan para registrar drivers de base de datos (import _ "github.com/lib/pq"), inicializar configuración global, o registrar providers.
34. ¿Qué es un closure en Go?
package main
import "fmt"
func makeCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
func main() {
counter := makeCounter()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2
counter2 := makeCounter()
fmt.Println(counter2()) // 1 — independiente
// Trap en loops — siempre hacer shadowing
funcs := make([]func(), 3)
for i := 0; i < 3; i++ {
i := i // shadowing
funcs[i] = func() { fmt.Println(i) }
}
for _, f := range funcs {
f() // 0, 1, 2
}
}35. ¿Qué es `io.Reader` e `io.Writer`?
package main
import (
"bufio"
"bytes"
"fmt"
"io"
"strings"
)
func countWords(r io.Reader) (int, error) {
scanner := bufio.NewScanner(r)
scanner.Split(bufio.ScanWords)
count := 0
for scanner.Scan() {
count++
}
return count, scanner.Err()
}
func main() {
n, _ := countWords(strings.NewReader("hola mundo como están todos"))
fmt.Println("Palabras:", n) // 5
src := strings.NewReader("contenido importante")
var dst bytes.Buffer
io.Copy(&dst, src)
fmt.Println(dst.String())
}Permiten escribir código que funciona con archivos, HTTP bodies, strings, buffers y sockets sin cambiar la función.
Recursos para seguir practicando
Si llegaste hasta acá, dominás la teoría. El siguiente paso es que alguien con experiencia real te haga las preguntas, te presione cuando te trabés, y te dé feedback honesto sobre tu código.
Para la entrevista de verdad:
- Andá a [InterviewHack.ai](https://interviewhack.ai) — generá tu dossier con las preguntas específicas de la empresa y el rol que querés. Las preguntas se generan a partir de tu CV real y de quién te va a entrevistar.
- Si tenés la entrevista en menos de 48 horas, reservá una sesión de práctica con un coach humano que ya leyó tu dossier.
El código lo sabés. Lo que falta es la presión del tiempo real y saber cómo comunicar lo que pensás mientras lo hacés.