Por qué Rust no tiene argumentos por defecto — y cómo solucionarlo
\n\nSi vienes a Rust desde Python, JavaScript o C++, seguramente te has encontrado con una sorpresa: Rust no tiene soporte incorporado para argumentos por defecto. No puedes simplemente escribir fn greet(name = "mundo") — el compilador te dirá que no.
¡Pero no te preocupes! Rust ofrece otros enfoques más flexibles y seguros. En este tutorial analizaremos 3 formas comprobadas de implementar el comportamiento de argumentos por defecto. Aprenderás a usar Option<T>, estructuras con configuración y el patrón Builder — y qué método elegir para tu tarea.
El artículo está basado en una pregunta real de Stack Overflow (más de 50 000 visitas) — la hemos adaptado para un formato educativo. ¡Allá vamos!
\n\nMétodo 1. Usamos Option<T> — la opción más simple
\n\nLa forma más directa es aceptar argumentos como Option<T> y dentro de la función sustituir valores por defecto.
fn greet(name: Option<&str>) {\n let name = name.unwrap_or("mundo");\n println!("¡Hola, {}!", name);\n}\n\nfn main() {\n greet(Some("Ana")); // ¡Hola, Ana!\n greet(None); // ¡Hola, mundo!\n}\n\n\nVentajas:
\n- \n
- Mínimo código \n
- Comprensible incluso para principiantes \n
- No requiere estructuras adicionales \n
Desventajas:
\n- \n
- Cada llamada requiere envolver en
Some(...)oNone\n - Si hay varios parámetros, el código se vuelve voluminoso \n
Este enfoque es ideal para funciones con 1-2 parámetros opcionales. Pero si hay más, la legibilidad se resiente.
\n\nMétodo 2. Estructura con configuración — limpio y escalable
\n\nCuando los parámetros se convierten en 3, 5 o 10, una estructura de configuración viene al rescate. Creas un tipo separado que almacena toda la configuración e implementas el trait Default para él.
struct GreetConfig {\n name: String,\n greeting: String,\n excitement_level: u8,\n}\n\nimpl Default for GreetConfig {\n fn default() -> Self {\n Self {\n name: "mundo".to_string(),\n greeting: "Hola".to_string(),\n excitement_level: 1,\n }\n }\n}\n\nfn greet(config: GreetConfig) {\n let excitement = "!".repeat(config.excitement_level as usize);\n println!("{}, {}{}", config.greeting, config.name, excitement);\n}\n\nfn main() {\n // Usamos valores por defecto\n greet(GreetConfig::default());\n // Salida: ¡Hola, mundo!\n\n // Cambiamos solo lo necesario\n greet(GreetConfig {\n name: "Ana".to_string(),\n ..Default::default()\n });\n // Salida: ¡Hola, Ana!\n\n // Llamada completamente personalizada\n greet(GreetConfig {\n name: "Mundo".to_string(),\n greeting: "Saludos".to_string(),\n excitement_level: 3,\n });\n // Salida: ¡Saludos, Mundo!!!\n}\n\n\nVentajas:
\n- \n
- Máxima flexibilidad \n
- Se pueden añadir nuevos campos sin cambiar la firma de la función \n
- La sintaxis
..Default::default()es muy legible \n
Desventajas:
\n- \n
- Requiere más código boilerplate \n
- Hay que implementar explícitamente el trait
Default\n
Esta es la elección ideal para proyectos reales: bibliotecas, funciones de configuración, APIs complejas.
\n\nMétodo 3. Builder Pattern — para quienes aman las cadenas
\n\nEl patrón Builder es un clásico del ecosistema Rust. Creas una estructura constructora que acumula parámetros y al final llama al método build().
struct Greeter {\n name: String,\n greeting: String,\n excitement_level: u8,\n}\n\nstruct GreeterBuilder {\n nam