Las subcadenas (slices) en Rust son tipos de datos sin propiedad que permiten referenciar secciones contiguas de colecciones sin tomar posesión de los datos originales. Este mecanismo resuelve problemas comunes relacionados con la validez de índices y sincronización de referencias.
Consideremos una implementación inicial para encontrar la primera palabra en un texto:
fn encontrar_palabra_completa(texto: &String) -> Option<usize> {
for (posicion, caracter) in texto.char_indices() {
if caracter.is_whitespace() {
return Some(posicion);
}
}
None
}
Este enfoque presenta riesgos de seguridad:
fn main() {
let mut contenido = String::from("Rust es genial");
if let Some(indice) = encontrar_palabra_completa(&contenido) {
contenido.clear(); // Modifica el estado original
// ¡El índice ya no es válido! Usarlo causaría comportamiento indefinido
}
}
La solución robusta utiliza subcadeans de texto (string slices), representadas como &str. Su sintaxis sigue el patrón [inicio..fin], donde:
- El índice inicial está incluido
- El índice final está excluido
Ejemplos prácticos:
let mensaje = String::from("Hola Mundo");
let saludo = &mensaje[0..4]; // "Hola"
let destino = &mensaje[5..10]; // "Mundo"
let completo = &mensaje[..]; // String completo
Las subcadenas garantizan coherencia mediante referencias válidas. Reimplementemos la función usando este principio:
fn primera_palabra(texto: &str) -> &str {
let mut longitud = 0;
for caracter in texto.chars() {
if caracter.is_whitespace() {
break;
}
longitud += caracter.len_utf8();
}
&texto[..longitud]
}
Este diseño ofrece ventajas clave:
- Funciona con literales de cadena (
let literal = "Texto fijo";) - Acepta tanto
Stringcomo&strcomo entrada - Evita problemas de índices desincronizados
La flexibilidad se evidencia en su uso:
fn main() {
let cadena = String::from("Ejemplo práctico");
let fragmento = primera_palabra(&cadena[..7]); // Subcadena parcial
let completa = primera_palabra(&cadena); // Cadena completa
let literal = "Texto estático";
let parte = primera_palabra(&literal[0..5]); // Funciona con literales
}
El sistema de slices no se limita a cadenas. También aplica a arreglos:
let numeros = [10, 20, 30, 40, 50];
let segmento = &numeros[1..4]; // Contiene [20, 30, 40]
Al utilizar slices, Rust garantiza que las referencias permanezcan válidas dentro de los límites definidos, evitando errores de acceso fuera de rango siempre que se respeten los límites de caracteres UTF-8.