Los slices (slice, tipos [T] y &[T]) fueron introducidos en Rust para un acceso seguro y eficiente a subconjuntos de arreglos, vectores y otras secuencias de elementos. Permiten evitar asignaciones y copias innecesarias de datos, proporcionando solo una "vista" o ventana a una parte de la colección. Esto es diferente tanto de los arreglos, cuyo tamaño es fijo en tiempo de compilación, como de las colecciones dinámicas, que almacenan un puntero y una longitud, pero poseen la memoria.
Al trabajar con arreglos y vectores en lenguajes sin un control estricto del tiempo de vida, se producen a menudo errores de desbordamiento, fugas de memoria y uso de punteros incorrectos. Es importante asegurarse de que al trabajar con subconjuntos de colecciones no se esté realizando copias ni perdiendo la seguridad de la memoria, lo que es especialmente relevante a nivel de sistema.
En Rust, un slice es un "puntero + longitud" a una parte de los datos, que no posee el contenido. Siempre van acompañados de un lifetime, y el compilador garantiza que el slice no sobrevivirá al original (array, Vec, String). Todo el trabajo con slices se realiza a través de métodos de acceso seguros, y cualquier desbordamiento provoca un panic en tiempo de ejecución.
Ejemplo de código:
let arr = [1, 2, 3, 4, 5]; let slice = &arr[1..4]; // [2,3,4] tipo: &[i32] let mut vec = vec![10, 20, 30]; let mut_slice: &mut [i32] = &mut vec[..2]; mut_slice[0] = 99; assert_eq!(vec, [99, 20, 30]);
Características clave:
¿Se puede crear un slice que exceda el tamaño del arreglo o vector original?
No. El compilador y el tiempo de ejecución garantizan que un slice solo se puede crear en índices válidos de los datos originales. Intentar salir de los límites provocará un panic.
let arr = [1, 2, 3]; let s = &arr[0..4]; // panic en ejecución
¿Son los slices propietarios autónomos de la memoria?
No. Los slices son solo una "ventana" a los datos, no poseen memoria. Intentar devolver un slice de una función si la fuente es local dará lugar a un error de tiempo de compilación.
fn give_slice() -> &[i32] { let arr = [1,2,3]; &arr[1..] } // error: arr no vive lo suficiente
¿Cuáles son las diferencias entre slices y Arrays en Rust a nivel de tipos y operaciones?
Un array tiene una longitud fija, conocida en tiempo de compilación, y se almacena completamente en el stack. Un slice puede tener cualquier longitud, determinada dinámicamente, y siempre almacena un puntero y una longitud.
let a: [u32; 3] = [1,2,3]; // Array de longitud fija let s: &[u32] = &a[..]; // Slice de cualquier tamaño
Un programador devolvió un slice de una función donde se creó un arreglo local. Tras salir de la función, el original se eliminó y el slice se convirtió en un puntero "colgado". Esto causó un bug e incluso una falla del programa.
Ventajas:
Desventajas:
Un slice siempre se crea referenciando datos externos, el propietario de los datos y el slice viven el mismo tiempo. El compilador garantiza una conexión estrecha en el tiempo de vida entre el slice y la fuente.
Ventajas:
Desventajas: