Introducción
Los tipos concretos son útiles cuando los datos tienen una única estructura fija, pero repetir tipos casi idénticos para números y texto dificulta el mantenimiento del programa. Ya ha utilizado tipos genéricos estándar como Option<u32>, Vec<String>, HashMap<String, u32> y Result<T, E>. En este laboratorio, definirá usted mismo un tipo genérico. Los genéricos de Rust permiten que una sola definición funcione con varios tipos concretos, mientras el compilador sigue comprobando cada uso.
En este laboratorio, convertirá la estructura Pair preparada en un tipo genérico y, después, generalizará un pequeño auxiliar que toma referencias. El programa más largo y los datos de ejemplo ya están escritos, por lo que su trabajo se centrará en las pocas posiciones de tipo que permiten reutilizar el código.
Convierta la estructura Pair en genérica
En este paso, reemplazará el tipo fijo i32 de la estructura por un parámetro de tipo para que la misma definición pueda contener puntuaciones o nombres de rutas.
Vaya al paquete de Cargo preparado:
cd /home/labex/project/pair-demo
Inspeccione el archivo fuente breve. El comando sed -n muestra el intervalo de líneas solicitado sin modificar el archivo:
sed -n '1,120p' src/main.rs
El valor scores contiene enteros, mientras que routes contiene valores String propios. La definición actual de Pair fija ambos campos como i32, por lo que no puede representar los dos valores. El atributo preparado #[derive(Debug)] generaría el formato de depuración para el tipo, pero el programa visible no utiliza ese formato; no forma parte de la tarea sobre genéricos.
Un tipo genérico introduce un marcador de posición, como T, entre corchetes angulares. Cada Pair construido sigue teniendo un tipo concreto: el compilador infiere Pair<i32> para scores y Pair<String> para routes.
Abra el código fuente en Nano:
nano src/main.rs
Realice estos tres pequeños cambios cerca del principio del archivo:
- Cambie
struct Pair {porstruct Pair<T> {. - Cambie el tipo de
leftdei32aT. - Cambie el tipo de
rightdei32aT.
La estructura completa debe quedar así:
struct Pair<T> {
left: T,
right: T,
}
Aquí, ambos campos utilizan el mismo T, por lo que un par no puede mezclar accidentalmente tipos de campo no relacionados. Guarde con Ctrl+O, pulse Enter y salga con Ctrl+X.
Pida a Cargo que compruebe los tipos del paquete:
cargo check
Una línea que termine en Finished indica que el compilador aceptó tanto Pair<i32> como Pair<String>. Ahora ejecute el programa:
cargo run --quiet
scores: 82 and 91
routes: north and east
first score: 82
Las dos primeras líneas demuestran que una sola definición de estructura ahora almacena dos tipos concretos diferentes. El auxiliar first existente todavía acepta únicamente Pair<i32>, y usted lo generalizará a continuación.
Generalice un auxiliar que toma referencias
En este paso, añadirá a la función first su propio parámetro de tipo para que pueda tomar una referencia a cualquier Pair<T> y devolver una referencia a su primer valor.
Inspeccione el auxiliar actual cerca del principio del archivo:
sed -n '1,30p' src/main.rs
Su entrada Pair<i32> y su salida &i32 hacen que sea específico para las puntuaciones. Una función genérica declara <T> después de su nombre y utiliza ese mismo T allí donde aparecen los tipos de entrada y salida relacionados.
Abra el archivo:
nano src/main.rs
Cambie únicamente la declaración de la función, de:
fn first(pair: &Pair<i32>) -> &i32 {
a:
fn first<T>(pair: &Pair<T>) -> &T {
La función toma una referencia al par y devuelve una referencia a un campo, por lo que no mueve ni copia el valor almacenado. Rust puede inferir T a partir de cada argumento; quienes llaman a la función no necesitan escribir explícitamente el tipo.
A continuación, reemplace // FIRST_ROUTE_REPORT por una llamada que demuestre que el auxiliar también funciona con texto:
println!("first route: {}", first(&routes));
Guarde los cambios y salga de Nano. Después, compruebe los tipos y ejecute el programa completo:
cargo check
cargo run --quiet
scores: 82 and 91
routes: north and east
first score: 82
first route: north
Las dos líneas first proceden de la misma función genérica. Rust infiere T = i32 para la llamada relacionada con las puntuaciones y T = String para la llamada relacionada con las rutas; después, comprueba cada llamada con ese tipo concreto. La reutilización no elimina la comprobación estática de tipos.
Resumen
Ha reemplazado un par concreto por Pair<T>, lo ha utilizado tanto con enteros como con cadenas y ha generalizado un auxiliar que toma referencias sin introducir traits ni duplicar implementaciones.


