Explorando los Tipos Asociados de Rust

Beginner

This tutorial is from open-source community. Access the source code

Introducción

En este laboratorio, exploramos el concepto de tipos asociados en Rust, que permite mejorar la legibilidad del código al definir tipos internos localmente dentro de un trato como tipos de salida. Esto se logra mediante el uso de la palabra clave type dentro de la definición del trato. Con tipos asociados, las funciones que usan el trato ya no necesitan expresar explícitamente los tipos A y B, lo que hace que el código sea más conciso y flexible. Reescribimos un ejemplo de la sección anterior usando tipos asociados para ilustrar su uso en la práctica.

Nota: Si el laboratorio no especifica un nombre de archivo, puede usar cualquier nombre de archivo que desee. Por ejemplo, puede usar main.rs, compilar y ejecutarlo con rustc main.rs &&./main.

Tipos asociados

El uso de "Tipos asociados" mejora la legibilidad general del código al mover los tipos internos localmente a un trato como tipos de salida. La sintaxis para la definición del trato es la siguiente:

// `A` y `B` se definen en el trato a través de la palabra clave `type`.
// (Nota: `type` en este contexto es diferente de `type` cuando se utiliza
// para alias).
trait Contains {
    type A;
    type B;

    // Sintaxis actualizada para referirse a estos nuevos tipos de manera genérica.
    fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
}

Tenga en cuenta que las funciones que usan el trato Contains ya no se requiere expresar A o B en absoluto:

// Sin usar tipos asociados
fn difference<A, B, C>(container: &C) -> i32 where
    C: Contains<A, B> {... }

// Usando tipos asociados
fn difference<C: Contains>(container: &C) -> i32 {... }

Vamos a reescribir el ejemplo de la sección anterior usando tipos asociados:

struct Container(i32, i32);

// Un trato que verifica si 2 elementos se almacenan dentro del contenedor.
// También recupera el primer o último valor.
trait Contains {
    // Defina tipos genéricos aquí que los métodos podrán utilizar.
    type A;
    type B;

    fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
    fn first(&self) -> i32;
    fn last(&self) -> i32;
}

impl Contains for Container {
    // Especifique qué tipos son `A` y `B`. Si el tipo `input`
    // es `Container(i32, i32)`, los tipos `output` se determinan
    // como `i32` e `i32`.
    type A = i32;
    type B = i32;

    // `&Self::A` y `&Self::B` también son válidos aquí.
    fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
        (&self.0 == number_1) && (&self.1 == number_2)
    }
    // Obtenga el primer número.
    fn first(&self) -> i32 { self.0 }

    // Obtenga el último número.
    fn last(&self) -> i32 { self.1 }
}

fn difference<C: Contains>(container: &C) -> i32 {
    container.last() - container.first()
}

fn main() {
    let number_1 = 3;
    let number_2 = 10;

    let container = Container(number_1, number_2);

    println!("¿Contiene el contenedor {} y {}: {}",
        &number_1, &number_2,
        container.contains(&number_1, &number_2));
    println!("Primer número: {}", container.first());
    println!("Último número: {}", container.last());

    println!("La diferencia es: {}", difference(&container));
}

Resumen

¡Felicidades! Has completado el laboratorio de Tipos Asociados. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.