Go: Cómo establecer valores predeterminados en estructuras con máxima eficiencia
\n\n¡Hola, desarrollador Go! Si alguna vez te has preguntado: "¿Por qué en Go no se puede simplemente escribir default: dentro de una estructura, como en Python o C++?", has llegado al lugar indicado. Este tema es una de las preguntas más candentes en Stack Overflow (¡decenas de miles de visitas!), y hoy lo resolveremos de una vez por todas.
En Go no existe un mecanismo incorporado para valores predeterminados en Go structs. Los creadores del lenguaje optaron conscientemente por esto para evitar la magia y las complejidades propias de otros lenguajes. En su lugar, ofrecen patrones elegantes y eficientes. Los veremos todos: desde constructores simples hasta técnicas avanzadas con opciones. Y, por supuesto, mediremos el rendimiento para que puedas elegir no solo un código bonito, sino también rápido.
\n\n¿Por qué Go no admite valores predeterminados "de fábrica"?
\n\nAntes de sumergirnos en el código, entendamos la filosofía. En Go, cada variable tiene un zero value (valor cero). Para int es 0, para string es "", para punteros y slices es nil. Esto se hizo para garantizar previsibilidad y seguridad. Si Go permitiera sobrescribir estos valores en la declaración de la estructura, generaría confusión: ¿cuál es el valor "real", el definido en la estructura o el que pasamos al inicializar?
Por lo tanto, la comunidad de Go ha desarrollado varias formas idiomáticas de resolver el problema de establecer valores predeterminados en estructuras Go. Analicémoslas de lo simple a lo complejo.
\n\nMétodo 1: Función constructora (New()) — el estándar de oro
\n\nEsta es la forma más popular y recomendada. En lugar de crear la estructura directamente, escribimos una función que devuelve una instancia con los campos ya asignados.
\n\npackage config\n\ntype AppConfig struct {\n Port int\n Timeout time.Duration\n Debug bool\n DBHost string\n}\n\n// NewAppConfig — constructor con valores predeterminados\nfunc NewAppConfig() AppConfig {\n return AppConfig{\n Port: 8080,\n Timeout: 30 * time.Second,\n Debug: false,\n DBHost: "localhost",\n }\n}\n\nUso:
\ncfg := config.NewAppConfig()\nfmt.Println(cfg.Port) // 8080\n\n¿Por qué es genial? Es explícito, legible y eficiente. El compilador de Go optimiza fácilmente este código. Sin reflexión, sin asignaciones innecesarias: simplemente devuelve la estructura por valor.
\n\nBenchmark (simplificado):
\nfunc BenchmarkNewAppConfig(b *testing.B) {\n for i := 0; i < b.N; i++ {\n _ = NewAppConfig()\n }\n}\n// Resultado: ~0.3 ns/op — prácticamente gratis\n\nMétodo 2: Opciones funcionales (Functional Options Pattern)
\n\nEste patrón se ha convertido en una seña de identidad de Go. Es ideal cuando la estructura tiene muchos campos, la mayoría opcionales. En lugar de pasar 10 argumentos al constructor, usamos funciones-opción.
\n\ntype Server struct {\n host string\n port int\n timeout time.Duration\n maxConn int\n}\n\ntype Option func(*Server)\n\nfunc WithHost(host string) Option {\n return func(s *Server) {\n s.host = host\n }\n}\n\nfunc WithPort(port int) Option {\n return func(s *Server) {\n s.port = port\n }\n}\n\nfunc WithTimeout(t time.Duration) Option {\n return func(s *Server) {\n s.timeout = t\n }\n}\n\n// NewServer — constructor con valores predeterminados\nfunc NewServer(opts ...Option) *Server {\n // Establecemos valores predeterminados en Go structs\n s := &Server{\n host: "0.0.0.0",\n port: 8080,\n timeout: 30 * time.Second,\n maxConn: 100,\n }\n // Aplicamos las opciones pasadas\n for _, opt := range opts {\n opt(s)\n }\n return s\n}\n\n