ProgramaciónDesarrollador Fullstack

¿Cómo funciona el trabajo con cadenas inmutables y el tipo dinámico String en Rust? ¿Cuáles son las diferencias entre String y &str, cómo funciona la propiedad y cómo convertir de manera segura entre estos tipos?

Supere entrevistas con el asistente de IA Hintsage

Respuesta.

Historia del problema:

A diferencia de los lenguajes nativos (C/C++), Rust construye un trabajo seguro con cadenas a través de una estricta separación entre tipos de referencia (&str) y tipos propietarios (String). Esto elimina la mayoría de los errores relacionados con la memoria incorrecta, desbordamientos de búfer y doble liberación.

Problema:

A diferencia de lenguajes con recolección de basura, donde cualquier cadena vive en memoria gestionada, en Rust hay que entender claramente quién posee la cadena, cuánto tiempo vive y cómo evitar referencias colgantes después de modificaciones. Trabajar con cadenas UTF-8 requiere cuidado al indexar y modificar.

Solución:

En Rust, String es una cadena mutable, asignada en el heap, que posee su contenido. &str es una referencia inmutable a una secuencia de bytes con garantía UTF-8. Si es necesario, se puede convertir de manera segura entre (&str -> String y viceversa) utilizando métodos de std.

Ejemplo de código:

fn main() { let owned: String = String::from("Rust"); let borrowed: &str = &owned; let primitive: &str = "Hello"; // literal siempre es &str // Conversión &str -> String let s: String = primitive.to_string(); // Conversión String -> &str let st: &str = &s; println!("{} {} {} {}", owned, borrowed, primitive, st); }

Características clave:

  • Separación explícita entre propiedad y referencia (heap vs slice)
  • Los métodos de conversiones seguras entre String y &str son eficientes y transparentes en cuanto al tiempo de vida del objeto
  • Los literales de cadena siempre tienen el tipo &'static str, no String

Preguntas tramposas.

¿Por qué no se puede indexar una cadena como s[1] o s[i]?

Las cadenas Rust están en UTF-8, por lo que la indexación no está disponible directamente: s[i] no devuelve el i-ésimo carácter y a veces puede provocar panic al acceder a límites de bytes incorrectos. En su lugar, use los métodos .chars().nth(i) o .get(start..end).

¿Se puede modificar &str de manera segura?

No se puede — &str siempre es un slice inmutable. Para modificaciones, use to_owned/to_string, o utilice String/Vec<u8>.

¿Cuál es la diferencia fundamental entre String::from("abc") y "abc".to_string()?

Estas variantes son equivalentes en el resultado, ambas crean String copiando datos de &str. La diferencia está en el estilo: por ejemplo, to_string se implementa a través del trait ToString, mientras que String::from expresa de manera más clara la intención de "crear propiedad".

Errores comunes y anti-patrones

  • Intentar indexar la cadena s[1] o s[0] para obtener char
  • Conversiones implícitas sin especificar el tiempo de vida: devolver referencias a objetos temporales
  • Usar String donde basta con &str (asignación innecesaria)

Ejemplo de la vida real

Caso negativo

La función aceptaba String y hacía una copia innecesaria de la cadena internamente (clone), luego escribía un slice en otra función, olvidando extender el tiempo de vida de la fuente. Resultado: referencia colgante y crash.

Pros:

  • De manera similar a los lenguajes familiares: se puede obtener fácilmente una "copia" de la cadena

Contras:

  • Pérdida de rendimiento por asignaciones innecesarias
  • Posible fuga de referencias a valores temporales

Caso positivo

La función acepta &str, si se necesitan modificaciones, internamente llama a .to_string(), y en el exterior, toda la lógica permanece "cero copia". Los tiempos de vida están bajo control, sin asignaciones innecesarias.

Pros:

  • Alto rendimiento
  • Se previenen errores de propiedad

Contras:

  • Es necesario familiarizarse con los tiempos de vida y la propiedad
  • Un poco más de carga cognitiva para los principiantes