Restrições de Tipos Genéricos em Rust

Beginner

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

Introdução

Neste laboratório, ao trabalhar com genéricos em Rust, os parâmetros de tipo devem ser delimitados por traits para especificar a funcionalidade que um tipo deve implementar.

Nota: Se o laboratório não especificar um nome de arquivo, você pode usar qualquer nome de arquivo que desejar. Por exemplo, você pode usar main.rs, compilar e executar com rustc main.rs && ./main.

Limites

Ao trabalhar com genéricos, os parâmetros de tipo frequentemente precisam de traits como limites para especificar qual a funcionalidade que um tipo implementa. Por exemplo, o exemplo a seguir utiliza o trait Display para impressão, e portanto exige que T seja limitado por Display; ou seja, T deve implementar Display.

// Define uma função `printer` que recebe um tipo genérico `T` que
// deve implementar o trait `Display`.
fn printer<T: Display>(t: T) {
    println!("{}", t);
}

A delimitação restringe o genérico a tipos que se ajustam aos limites. Ou seja:

struct S<T: Display>(T);

// Erro! `Vec<T>` não implementa `Display`. Esta
// especialização falhará.
let s = S(vec![1]);

Outro efeito da delimitação é que as instâncias genéricas podem aceder aos [métodos] dos traits especificados nos limites. Por exemplo:

// Um trait que implementa o marcador de impressão: `{:?}`.
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 }

// O genérico `T` deve implementar `Debug`. Independentemente
// do tipo, isto funcionará corretamente.
fn print_debug<T: Debug>(t: &T) {
    println!("{:?}", t);
}

// `T` deve implementar `HasArea`. Qualquer tipo que satisfaça
// o limite pode aceder à função `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!("Área: {}", area(&rectangle));

    //print_debug(&_triangle);
    //println!("Área: {}", area(&_triangle));
    // ^ TODO: Tente descomentar isto.
    // | Erro: Não implementa `Debug` nem `HasArea`.
}

Como nota adicional, as cláusulas where também podem ser usadas para aplicar limites em alguns casos para serem mais expressivas.

Resumo

Parabéns! Você concluiu o laboratório de Limites. Pode praticar mais laboratórios no LabEx para melhorar suas habilidades.