¿Por qué Rust no permite simplemente comparar String con "hola"?
\n\nSi recién está comenzando a aprender Rust y ya se ha enfrentado a la necesidad de comparar una variable de tipo String con algún literal de cadena como "hello" o "world", seguramente ha visto un extraño error del compilador. Escribe algo como:
let user_input = String::from("start");\nmatch user_input {\n "start" => println!("¡Comenzamos!"),\n "stop" => println!("¡Nos detenemos!"),\n _ => println!("Comando desconocido"),\n}\n\n\nY el compilador muestra algo como: "expected struct `String`, found `&str`". ¿Le suena familiar?
\n\nEn Stack Overflow, la pregunta "How to match a String against string literals?" ha acumulado miles de visitas: es un verdadero dolor para los principiantes. En este artículo, analizaremos por qué sucede esto y mostraremos tres formas sencillas de resolver el problema. Aprenderá cómo funciona el sistema de propiedad y préstamo en Rust en la práctica y dejará de temer los errores del compilador.
\n\nNaturaleza del conflicto: String vs &str
\n\nPara entender el problema, debemos recordar los conceptos básicos. En Rust hay dos tipos principales para trabajar con texto:
\n\n- \n
String— es una cadena propietaria que se almacena en el heap. Puede modificarse, crecer y reducirse. Ejemplo:let s = String::from("hello");\n &str— es un slice de cadena. Es una referencia a una parte de una cadena (o a un literal de cadena que se almacena directamente en el binario del programa). Ejemplo:"hello"es precisamente&'static str. \n
Cuando escribe match user_input { "start" => ... }, está intentando comparar String con &str. El compilador dice: "No sé cómo compararlos directamente, ¡los tipos son diferentes!". Y esto es bueno: Rust lo protege de conversiones implícitas que podrían provocar pérdida de rendimiento o errores ocultos.
Pero, ¿cómo se soluciona entonces? Hay tres soluciones simples y elegantes.
\n\nSolución 1: Préstamo mediante as_str()
\n\nLa forma más directa y comprensible es obtener un slice de cadena (&str) a partir de String usando el método as_str(). Este método devuelve una referencia al contenido de la cadena, sin copiarla. Se ve así:
fn main() {\n let command = String::from("start");\n\n match command.as_str() {\n "start" => println!("Iniciando sistema..."),\n "stop" => println!("Deteniendo..."),\n "pause" => println!("Pausa..."),\n _ => println!("Comando desconocido: {}", command),\n }\n}\n\n\n¿Por qué funciona? as_str() devuelve un &str, que ahora se puede comparar con literales. Rust ve que ambos operandos son &str y el pattern matching funciona perfectamente.
Ventajas: simplicidad, legibilidad, costo cero (préstamo, no copia).
\n\nDesventajas: hay que recordar llamar al método. Pero es un detalle menor.
\n\nSolución 2: Comparación en condición if
\n\nSi no necesita toda la potencia de match, puede usar if normales con el método eq() o el operador ==. Rust puede comparar String y &str a través del trait PartialEq, que está implementado para este par de tipos.
fn main() {\n let user_input = String::from("admin");\n\n if user_input == "admin" {\n println!("¡Acceso concedido!");\n } else if user_input == "guest" {\n println!("Acceso de invitado");\n } else {\n println!("Usuario incorrecto");\n }\n}\n\n\nEsto funciona porque el compilador convierte automáticamente String a &str al comparar. Pero en match no existe tal conversión: allí se requiere una coincidencia exacta.