Restricciones de Tipo de Genéricos en Rust

Beginner

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

Introducción

En este laboratorio, al trabajar con genéricos en Rust, los parámetros de tipo deben estar limitados por traits para especificar la funcionalidad que un tipo debe implementar.

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.

Límites

Al trabajar con genéricos, los parámetros de tipo a menudo deben usar traits como límites para especificar qué funcionalidad implementa un tipo. Por ejemplo, el siguiente ejemplo utiliza el trait Display para imprimir y por lo tanto requiere que T esté limitado por Display; es decir, T debe implementar Display.

// Define una función `printer` que toma un tipo genérico `T` que
// debe implementar el trait `Display`.
fn printer<T: Display>(t: T) {
    println!("{}", t);
}

Establecer límites restringe el genérico a tipos que cumplen con los límites. Es decir:

struct S<T: Display>(T);

// Error! `Vec<T>` no implementa `Display`. Esta
// especialización fallará.
let s = S(vec![1]);

Otro efecto de establecer límites es que se permite a las instancias genéricas acceder a los [métodos] de los traits especificados en los límites. Por ejemplo:

// Un trait que implementa el marcador de impresión: `{:?}`.
use std::fmt::Debug;

trait HasArea {
    fn area(&self) -> f64;
}

impl HasArea for Rectangle {
    fn area(&self) -> f64 { self.length * self.height }
}

#[derive(Debug)]
struct Rectangle { length: f64, height: f64 }
#[allow(dead_code)]
struct Triangle  { length: f64, height: f64 }

// El genérico `T` debe implementar `Debug`. Independientemente
// del tipo, esto funcionará correctamente.
fn print_debug<T: Debug>(t: &T) {
    println!("{:?}", t);
}

// `T` debe implementar `HasArea`. Cualquier tipo que cumpla
// con el límite puede acceder a la función `area` de `HasArea`.
fn area<T: HasArea>(t: &T) -> f64 { t.area() }

fn main() {
    let rectangle = Rectangle { length: 3.0, height: 4.0 };
    let _triangle = Triangle  { length: 3.0, height: 4.0 };

    print_debug(&rectangle);
    println!("Area: {}", area(&rectangle));

    //print_debug(&_triangle);
    //println!("Area: {}", area(&_triangle));
    // ^ TODO: Intentar descomentar estos.
    // | Error: No implementa ni `Debug` ni `HasArea`.
}

Como nota adicional, en algunos casos también se pueden usar cláusulas where para aplicar límites y ser más expresivos.

Resumen

¡Felicitaciones! Has completado el laboratorio de Límites. Puedes practicar más laboratorios en LabEx para mejorar tus habilidades.