impl Trait

imple Trait es similar a los genéricos. Los genéricos usan un tipo, representado (por convenio) por T o similar, que se decide en tiempo de compilación.

En primer lugar, se presenta un ejemplo con un tipo concreto:

fn gives_higher_i32(one: i32, two: i32) {
    let higher = if one > two { one } else { two };
    println!("{} is higher.", higher);
}

fn main() {
    gives_higher_i32(8, 10);
}

Que imprime: 10 is higher..

En este caso, la función solo recibe el tipo i32. A continuación, se modifica el código para hacerlo genérico. El código de la función necesita comparar y necesita imprimir con {}, por lo que el tipo T necesita implementar los rasgos PartialOrd y Display. Se debe recordar que esto significa que solo es posible implementar esta función para aquellos tipos que tengan estos rasgos.

use std::fmt::Display;

fn gives_higher_i32<T: PartialOrd + Display>(one: T, two: T) {
    let higher = if one > two { one } else { two };
    println!("{} is higher.", higher);
}

fn main() {
    gives_higher_i32(8, 10);
}

El caso de impl Trait es similar, pero decide en tiempo de ejecución. Permite pasar variables de los tipos que implementen el rasgo, pero el código de la función es uno solo (no se desarrolla uno para cada tipo en tiempo de ejecución). El código es un poco menos eficiente, ya que necesita determinar en tiempo de ejecución si el tipo que se pasa implementa o no los rasgos necesarios.

fn prints_it(input: impl Into<String> + std::fmt::Display) { // Takes anything that can turn into a String and has Display
    println!("You can print many things, including {}", input);
}

fn main() {
    let name = "Tuon";
    let string_name = String::from("Tuon");
    prints_it(name);
    prints_it(string_name);
}

Sin embargo, lo más interesante de esto es que se puede devolver también impl Trait y que esto permite devolver cierres (hay que recordar que hay tres tipos de cierres, pero cada implementación concreta tiene un tipo concreto único que es de uno de los tres generales). La definición de cada cierre es un rasgo en sí (trait).

Para entenderlo mejor, se puede observar algún método que funciona así en la librería estándar. Por ejemplo, esta es la definición de .map():

#![allow(unused)]
fn main() {
fn map<B, F>(self, f: F) -> Map<Self, F>     // 🚧
    where
        Self: Sized,
        F: FnMut(Self::Item) -> B,
    {
        Map::new(self, f)
    }
}

fn map<B, F>(self, f: F) significa que esta función necesita dos tipos genéricos. F es una función que recibe como parámetro un elemento del contenedor que implementa .map() y B es el valor de retorno de esta función. En el where se observan los ragos que deben tener las diferentes variables y tipos. Self tiene que ser un tipo Sized y F tiene que ser una función/cierre FnMut.

Así, se puede hacer lo mismo para devolver un cierre. Se usa impl y la definición del cierre. Una vez devuelto, se puede usar como cualquier otra función. A continuación se presenta un ejemplo de una función que devuelve diferentes cierres en función del texto que reciba. Si se pasa "double" o "triple" devuelve un cierre que multiplica por dos o por tres. En todos los demás casos, usa un cierre que devuelve el mismo valor:

fn returns_a_closure(input: &str) -> impl FnMut(i32) -> i32 {
    match input {
        "double" => |mut number| {
            number *= 2;
            println!("Doubling number. Now it is {}", number);
            number
        },
        "triple" => |mut number| {
            number *= 40;
            println!("Tripling number. Now it is {}", number);
            number
        },
        _ => |number| {
            println!("Sorry, it's the same: {}.", number);
            number
        },
    }
}

fn main() {
    let my_number = 10;

    // Make three closures
    let mut doubles = returns_a_closure("double");
    let mut triples = returns_a_closure("triple");
    let mut quadruples = returns_a_closure("quadruple");

    doubles(my_number);
    triples(my_number);
    quadruples(my_number);
}

Por último, se presenta un ejemplo más largo. Se imagina un juego en el que el personaje se enfrenta a monstruos que son más fuertes por la noche. Se puede crear un enumerado denominado TimeOfday para conocer el momento del día. El personaje se llama Simón y tiene un número que se denomina character_fear, que es un f64. El número sube por la noche (tiene más miedo por la noche) y baja durante el día. Se construye una función change_fear que cambia el el miedo en función del momento del día y hace otras cosas como imprimir mensajes:

enum TimeOfDay { // enum
    Dawn,
    Day,
    Sunset,
    Night,
}

fn change_fear(input: TimeOfDay) -> impl FnMut(f64) -> f64 { // la función recibe TimeOfDay. Devuelve un closure.
                // Se usa impl FnMut(64) -> f64 para indicar la función que
                // cambiará el valor
    use TimeOfDay::*; // Para que sea más corto de escribir Dawn, Day, Sunset, Night
                      // En lugar de TimeOfDay::Dawn, TimeOfDay::Day, etc.
    match input {
        Dawn => |x| { // x representa a la variable character_fear que se pasará después
            println!("The morning sun has vanquished the horrible night. You no longer feel afraid.");
            println!("Your fear is now {}", x * 0.5);
            x * 0.5
        },
        Day => |x| {
            println!("What a nice day. Maybe put your feet up and rest a bit.");
            println!("Your fear is now {}", x * 0.2);
            x * 0.2
        },
        Sunset => |x| {
            println!("The sun is almost down! This is no good.");
            println!("Your fear is now {}", x * 1.4);
            x * 1.4
        },
        Night => |x| {
            println!("What a horrible night to have a curse.");
            println!("Your fear is now {}", x * 5.0);
            x * 5.0
        },
    }
}

fn main() {
    use TimeOfDay::*;
    let mut character_fear = 10.0; // Comienza Simon a 10

    let mut daytime = change_fear(Day); // Crea cuatro cierres que cambian el nivel de miedo de Simon.
    let mut sunset = change_fear(Sunset);
    let mut night = change_fear(Night);
    let mut morning = change_fear(Dawn);

    character_fear = daytime(character_fear); // Llama a los cierres. Cambian el miedo y escriben un mensaje
    character_fear = sunset(character_fear);
    character_fear = night(character_fear);
    character_fear = morning(character_fear);
}

Que imprime:

What a nice day. Maybe put your feet up and rest a bit.
Your fear is now 2
The sun is almost down! This is no good.
Your fear is now 2.8
What a horrible night to have a curse.
Your fear is now 14
The morning sun has vanquished the horrible night. You no longer feel afraid.
Your fear is now 7