jueves, 11 de abril de 2019

Creando videojuegos en Rust lang (Parte 17)

Programa 11. "pre_hello_ggez11" Tipos de dato genéricos: "generics" o "<...>"
En algunas situaciones queremos crear varios structs o enums similares en forma pero con diferentes tipos de datos (eg. ( ), i32, i64, f32, f64, String, etc.) En estos casos esto llevaría a mucho código repetido, ya que tendríamos que hacer una copia para cada tipo de dato. Para evitar el código repetido Rust tiene un sistema para definir datos genéricos ("generics") en donde definimos la estructura pero dejamos libre el tipo de dato que puede contener. Luego al usarlos podemos asignar un tipo de dato concreto.

Para definir las variables genéricas usaremos los símbolos de mayor que y menor que. En medio de los símbolos podemos un identificador (palabra) en CamelCase que representa nuestra variable genérica. Por convención usaremos "T" que viene de "tipo" escrito en CamelCase. Para variables adicionales usamos las letras siguientes: "U", "V", etc.

Los tipos de datos genéricos también nos ayudaran a definir funciones genéricas y facilitan el uso de librerías/crates ya que podemos usar structs genéricos como contenedores de nuestros structs concretos. De la misma forma aunque tengamos structs genéricos podemos definir implementaciones ("impl") específicas para cada tipo de dato. No entraremos mucho a detalle a este último uso con los traits y sólo mostraré ejemplos de lo siguiente:
  • Usando Vec<T>. Vec es un objeto que viene incluido en las librerías predeterminadas de Rust. Este objeto es básicamente un vector y se le pueden aplicar las operaciones usuales de un vector. Se puede crear un Vec de diferentes tipos y podemos usar la macro "vec!" para crear vectores a partir de arreglos. Otros objetos definidos por paquetes también usan valores genéricos para exponer su funcionalidad a varios usuarios.
  • Usando structs genéricos. Podemos usar T, U, V, etc. para definir específicamente si esperamos que sean del mismo tipo o si permiten que sean tipos diferentes.
  • Usando funciones genéricas. Similar al caso anterior podemos definir lo que esperamos y el compilador no nos causará problemas al llamar la función siempre y cuando se emparejen con la definición genérica.
El código es el siguiente:


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#[derive(Debug)]
struct StructGenerico<T> {
    x: T,
    y: Vec<T>
}

#[derive(Debug)]
struct StructGenerico2<T, U> {
    x: T,
    y: U
}

fn fn_generica1<T>(_x: T) { 
    println!("Procesando un tipo");
}

fn fn_generica2<T>(_x: T, _y: T) { 
    println!("Procesando dos tipos iguales");
}

fn fn_generica3<T, U>(_x: T, _y: U) { 
    println!("Procesando dos tipos que pueden ser diferentes");
}

fn main() {
    let vec1 : Vec<i32> = vec![5,4,3,2,1,0];
    let vec2 : Vec<f32> = vec![1.01, 2.34, 5.67];
    let vec3 : Vec<()> = vec![(), ()];
    println!("vec1.len() = {:?} y contenido {:?}", vec1.len(), vec1);
    println!("vec2.len() = {:?} y contenido {:?}", vec2.len(), vec2);
    println!("vec3.len() = {:?} y contenido {:?}", vec3.len(), vec3);
    let mi_generico1: StructGenerico<i32> = StructGenerico::<i32>{
        x: 10, y: vec![1,2,3,4]};
    let mi_generico2: StructGenerico<f64> = StructGenerico::<f64>{
        x: 3.14, y: vec![1.1,2.2,3.3]};
    let mi_generico3: StructGenerico<()> = StructGenerico::<()>{
        x: (), y: vec![(),(),(),()]};
    println!("Entero mi_generico1 = {:?}", mi_generico1);
    println!("Double mi_generico2 = {:?}", mi_generico2);
    println!("Unidad mi_generico3 = {:?}", mi_generico3);
    let mi_generico4 : StructGenerico2<i32, String> = StructGenerico2{
        x: 10, y: String::from("hola")};
    let mi_generico5 : StructGenerico2<(), f32> = StructGenerico2{
        x: (), y: 5.43};
    println!("mi_generico4 = {:?}", mi_generico4);
    println!("mi_generico5 = {:?}", mi_generico5);
    fn_generica1(7);
    fn_generica1(3.1416);
    fn_generica2(1,3);
    fn_generica2("string1","string2");
    fn_generica3(7,"hola");
    fn_generica3(vec![1,2],6.3982);
}

Y el resultado de correr el código es el siguiente:

Navegación:
Primera parte
Siguiente parte
Parte anterior

Fuente (inglés):

No hay comentarios.:

Publicar un comentario