Un slice es una referencia a una secuencia de elementos contiguos en memoria. A diferencia de una referencia a un tipo único (como &i32), un slice es lo que se conoce como un “fat pointer” (puntero gordo). Esto significa que, internamente, un slice &[T] no es solo una dirección de memoria, sino una estructura compuesta por dos “words”: un puntero al inicio de la secuencia y una longitud que indica cuántos elementos contiene. Esta estructura permite que el programa acceda a una parte de un contenedor sin necesidad de conocer la capacidad total o el contenedor original, garantizando seguridad mediante la verificación de límites.
En el diseño de APIs, es una práctica fundamental preferir el uso de slices (&[T]) en lugar de referencias a contenedores específicos como &Vec<T> o &[T; N]. Esto se debe a la coerción de desreferencia (Deref coercion). Gracias a que Vec<T> implementa el trait Deref<Target = [T]>, el compilador de Rust puede convertir automáticamente una referencia a un vector (&Vec<T>) en una referencia a un slice (&[T]). Lo mismo ocurre con &String convirtiéndose en &str. Al aceptar un slice, su función se vuelve extremadamente flexible: puede recibir un Vec, un array o incluso un subconjunto de otro slice, permitiendo una reutilización de código superior. Si se cometiera el error de diseñar una función que solo acepte &Vec<T>, se estaría limitando innecesariamente al usuario, obligándolo a empaquetar arrays en vectores para poder llamar a la función.
Los slices ofrecen un conjunto de métodos altamente optimizados para la manipulación de secuencias, tales como sort para ordenar, windows(n) para obtener iteradores sobre vistas deslizantes, o chunks(n) para dividir la secuencia en trozos de tamaño fijo.
fn main() {
let vector = vec![40, 10, 30, 20, 50];
let array = [5, 4, 3, 2, 1];
let mut datos_mutables = [10, 50, 20, 40, 30];
// Coerción de Vec<T> a &[i32]
imprimir_detalles(&vector);
// Coerción de Array a &[i32] y uso de sub-slice
imprimir_detalles(&array[1..4]);
// Uso de métodos de ventana (sliding windows)
mostrar_ventanas(&vector);
// Manipulación directa sobre el slice mutable
procesar_secuencia(&mut datos_mutables);
}
fn imprimir_detalles(slice: &[i32]) {
println!("Longitud del slice: {}, Primer elemento: {}", slice.len(), slice[0]);
}
fn mostrar_ventanas(slice: &[i32]) {
// windows(2) crea iteradores sobre elementos contiguos de tamaño 2
for ventana in slice.windows(2) {
println!("Ventana actual: {:?}", ventana);
}
}
fn procesar_secuencia(slice: &mut [i32]) {
// El slice permite ordenar los elementos directamente en su ubicación de memoria
slice.sort();
println!("Slice ordenado: {:?}", slice);
if slice.contains(&30) {
println!("El valor 30 fue encontrado.");
}
}
Explicación del Código
vector: Una instancia deVec<i32>. Al pasarse aimprimir_detalles, ocurre la Deref coercion, transformándose en un&[i32].array: Un array de tipo[i32; 5]. Al usar&array[1..4], se crea un slice que apunta desde el índice 1 hasta el 3.imprimir_detalles(slice: &[i32]): Esta función recibe un “fat pointer”. Dentro,slice.len()accede a la segunda parte del puntero (la longitud) yslice[0]utiliza el puntero base para acceder al primer elemento.mostrar_ventanas(slice: &[i32]): Utiliza el métodowindows(2)sobre el slice para crear iteradores de pares contiguos.datos_mutables: Un array que se pasa como una referencia mutable&mut [i32].procesar_secuencia(slice: &mut [i32]):slice.sort(): Aplica un algoritmo de ordenamiento directamente sobre la memoria referenciada por el puntero.slice.contains(&30): Utiliza un método de búsqueda para verificar la presencia de un valor dentro de los límites definidos por el slice.
N° 34