Bienvenidos sean a este post, hoy veremos la aplicacion de estos dos conceptos.
En el post anterior hablamos sobre los trait y en este post hablamos sobre que son los tipos genericos, estos ultimos tambien se pueden aplicar a los trait, si volvemos al ejemplo del post anterior se daran cuenta que es muy poco practico porque tenemos repetidos elementos cuando podemos transformarlo en uno solo por medio de genericos, para eso vamos a ir al ejemplo del post anterior y modificarlo.
Nota: Si no pasaron por el post, simplemente hagan un nuevo codigo llamado rasgo.
En nuestro ejemplo iremos al archivo main.rs y modificaremos el codigo existente con el siguiente:
struct Forma<T>
{
linea_uno: T,
linea_dos: T,
}
trait Calcular<T>
{
fn calc(&self) -> T;
}
impl Calcular<i32> for Forma<i32>
{
fn calc(&self) -> i32
{
self.linea_uno * 2 + self.linea_dos * 2
}
}
fn main()
{
let peri_int = Forma { linea_uno: 5, linea_dos: 12 };
println!(
"Linea uno = {}, Linea dos = {}, Perimetro = {}",
peri_int.linea_uno, peri_int.linea_dos, peri_int.calc()
);
}
Si observan reemplazamos a los dos struct originales:
struct Perimetro
{
tam_uno: i32,
tam_dos: i32,
}
struct Ovalo
{
radio: f32,
altura: f32,
}
Por este struct:
struct Forma<T>
{
linea_uno: T,
linea_dos: T,
}
Si observamos en el codigo original teniamos dos struct con las mismas propiedades con distintos nombres y distintos tipos pero dos por cada struct, en el codigo nuevo lo reemplazamos por uno solo de tipo generico donde tenemos dos propiedades los cuales se ajustaran de acuerdo al momento de crear nuestro objeto, lo siguiente fue reemplazar estos dos trait:
trait CalcPerim
{
fn calc_perim(&self) -> i32;
}
trait CalcOval
{
fn calc_oval(&self) -> f32;
}
Por el siguiente:
trait Calcular<T>
{
fn calc(&self) -> T;
}
Al igual que en el caso anterior en lugar de tener un trait para cada tipo le pasamos uno generico y este sera el tipo que devolveremos, luego teniamos estos dos impl:
impl CalcPerim for Perimetro
{
fn calc_perim(&self) -> i32
{
self.tam_uno * 2 + self.tam_dos * 2
}
}
impl CalcOval for Ovalo
{
fn calc_oval(&self) -> f32
{
3.1415927f32 * self.radio * self.altura
}
}
Y lo reemplazamos por este:
impl Calcular<i32> for Forma<i32>
{
fn calc(&self) -> i32
{
self.linea_uno * 2 + self.linea_dos * 2
}
}
Aca definimos a Calcular pero lo hacemos solo para los de tipo i32, al igual que usamos para definir el perimetro en el codigo anterior, por el momento no calculamos el ovalo, por ultimo tenemos el main donde en lugar de crear dos objetos creamos uno solo y mostramos todos los datos, compilemos y veamos su salida:
tinchicus@dbn001vrt:~/lenguajes/rust/rasgo$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.01s
Running `target/debug/rasgo`
Lado 1 = 5, Lado 2 = 12, Perimetro = 34
tinchicus@dbn001vrt:~/lenguajes/rust/rasgo$
Todo bien pero en este codigo perdimos el calculo del ovalo, para ello debemos hacer un par de modificaciones, la primera sera agregar el siguiente bloque despues de la definicion del impl:
impl Calcular<f32> for Forma<f32>
{
fn calc(&self) -> f32
{
3.1415927f32 * self.linea_uno * self.linea_dos
}
}
Este bloque es muy parecido a la definicion de impl para el tipo i32 pero esta vez agregamos al tipo f32 y cambiamos la formula para el calculo de otro tipo, la cual equivale al ovalo que vimos del codigo anterior, si lo tenemos que comparar con otros lenguajes es muy similar a lo que se llama sobrecarga, es decir la capacidad que tiene el lenguaje de tener funciones con el mismo nombre pero que manejan distintos tipos, continuando con nuestro codigo la siguiente modificacion sera agregar este nuevo objeto en el main:
let peri_flt = Forma { linea_uno: 5.1f32, linea_dos: 12.3f32 };
Este es un nuevo objeto de Forma pero para aplicar a la nueva funcion, por ultimo agregaremos el siguiente bloque:
println!(
"Linea uno = {}, Linea dos = {}, Perimetro = {}",
peri_flt.linea_uno, peri_flt.linea_dos, peri_flt.calc()
);
Simplemente mostramos los datos de nuestro nuevo objeto, con todo esto comentado podemos pasar a compilarlo y ver como funciona:
tinchicus@dbn001vrt:~/lenguajes/rust/rasgo$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.02s
Running `target/debug/rasgo`
Linea uno = 5, Linea dos = 12, Perimetro = 34
Linea uno = 5.1, Linea dos = 12.3, Perimetro = 197.07211
tinchicus@dbn001vrt:~/lenguajes/rust/rasgo$
Si lo comparamos ahora si volvimos a obtener un codigo que trabaja de la misma forma que vimos en el post anterior pero de una forma mas simple y que pone en practica uno de los paradigma en programacion como es la reutilizacion de codigo sin necesidad de repetirlo, antes de finalizar veamos como quedo el nuevo codigo final:
main.rs
struct Forma<T>
{
linea_uno: T,
linea_dos: T,
}
trait Calcular<T>
{
fn calc(&self) -> T;
}
impl Calcular<i32> for Forma<i32>
{
fn calc(&self) -> i32
{
self.linea_uno * 2 + self.linea_dos * 2
}
}
impl Calcular<f32> for Forma<f32>
{
fn calc(&self) -> f32
{
3.1415927f32 * self.linea_uno * self.linea_dos
}
}
fn main()
{
let peri_int = Forma { linea_uno: 5, linea_dos: 12 };
let peri_flt = Forma { linea_uno: 5.1f32, linea_dos: 12.3f32 };
println!(
"Linea uno = {}, Linea dos = {}, Perimetro = {}",
peri_int.linea_uno, peri_int.linea_dos, peri_int.calc()
);
println!(
"Linea uno = {}, Linea dos = {}, Perimetro = {}",
peri_flt.linea_uno, peri_flt.linea_dos, peri_flt.calc()
);
}
En resumen, hoy hemos visto trait con genericos, como combinarlos, hemos visto como modificar un codigo con trait a uno con trait y genericos, hemos visto como se puede simplificar algunas secciones, cuales son sus ventajas, tambien hemos visto como se hace una sobrecarga de impl y por ultimo hemos visto como quedo nuestro codigo mas reducido y practico, espero les haya sido de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donación
Es para mantenimento del sitio, gracias!
$1.50
