Un slice en Rust es una representación dinámica de una parte de una colección cujos elementos están dispuestos en memoria de forma secuencial. Un ejemplo típico de un slice es &[T] o &mut [T]. Los slices no poseen datos, sino que hacen referencia a un búfer externo. Todos los slices tienen una longitud que se almacena junto con la referencia.
Tipos principales:
&[T] — slice inmutable&mut [T] — slice mutable&str (de hecho — un slice de bytes, siempre válido en UTF-8)La seguridad de los slices se garantiza a nivel de compilador y en tiempo de ejecución: cualquier intento de acceder a un elemento fuera del slice provocará un pánico en ejecución, no habrá ningún error de memoria irreconciliable como en C/C++.
Ejemplo:
let v = vec![1, 2, 3, 4, 5]; let s: &[i32] = &v[1..4]; // slice, hace referencia a los elementos 2, 3, 4 println!("{:?}", s); // [2, 3, 4] // s[3]; // panic! (salida de los límites)
¿Pueden los slices estar vacíos? ¿Cuál es la diferencia entre un slice vacío y None?
Respuesta: Sí, los slices pueden estar vacíos (&[]), esto significa una referencia a una parte de datos con longitud cero, pero no es un análogo de None. Un slice vacío es seguro de usar, y Option<&[T]> se utiliza para distinguir "si hay un slice en absoluto".
Ejemplo:
let s: &[i32] = &[]; assert!(s.is_empty()); // Option<&[i32]> se utiliza si el slice puede no existir en absoluto.
Historia
En un gran servicio de registro, un programador tomó un slice basado en un rango calculado dinámicamente a partir de datos del usuario. Con un cálculo erróneo del slice (por ejemplo, cuando start > end o si end > len), el código funcionó en las pruebas, pero en producción provocó un "panic" y detuvo el proceso durante picos de carga.
Historia
En una biblioteca interna de hashing concurrente, utilizaron &mut [T] y varios hilos tomaron diferentes slices de un mismo array. Un hilo modificaba el slice, mientras que otro tomaba otro slice en la misma memoria. El programa se compilaba, pero debido a una división incorrecta podría haber habido UB a través de código inseguro (uso de unsafe), si los slices se superponían.
Historia
En un analizador de paquetes de red, se creaba un slice de manera insegura (puntero raw y from_raw_parts). El desarrollador olvidó verificar la validez de la longitud del paquete de entrada. Como resultado, el intento de lectura fuera de los límites llevó a la caída de la aplicación y a una vulnerabilidad (acceso potencial fuera de límites), que podría haber sido evitada con el uso correcto de slices seguros.