Traits Turbofish

Turbofish в типажах

Пример типажа

Обозначим типаж “летать” для супергероя и ведьмы:

// Anyone with the "Flying" badge must have a method called fly
trait Flying {
    fn fly(&self); // Takes a reference to itself, returns nothing
}

// Trait implementation
// Our hero
struct SuperHero {
    name: String,
}

// Our enemy
struct WickedWitch {
    name: String,
}

// We give both the Flying badge, but they implement it differently
impl Flying for SuperHero {
    fn fly(&self) {
        println!("{} soars through the sky majestically!", self.name);
    }
}

impl Flying for WickedWitch {
    fn fly(&self) {
        println!("{} cackles loudly as her broomstick rattles into the air!", self.name);
    }
}

// ONE function, that works on ANYTHING with the Flying badge:

// This function doesn't care if it's a hero or a witch.
// It just says: "Bring me anything with the 'Flying' badge."
fn race_to_the_castle(flyer: &impl Flying) {
    println!("The race starts!");
    flyer.fly(); // We know it can fly, because it has the badge!
    println!("We made it to the castle!");
}

fn main() {
    let hero = SuperHero { name: String::from("Captain Soar") };
    let witch = WickedWitch { name: String::from("Elvira") };

    // Both work perfectly!
    race_to_the_castle(&hero);
    race_to_the_castle(&witch);
}

Turbofish

При этом можно написать функцию race_to_the_castle двумя способами:

// короткий способ, использовался ранее:
fn race_to_the_castle(flyer: &impl Flying) {
    flyer.fly();
}

// Способ turbofish way (полный generic syntax):
fn race_to_the_castle<F: Flying>(flyer: &F) {
    flyer.fly();
}

Короткий способ подходит для простых случаев, но ломается на сложных. Примеры, когда нужно переходить на turbofish:

Несколько параметров должны быть ОДИНАКОВОГО типа

Нужны два животных для битвы, и они обязаны быть одинакового типа:

// SHORTHAND: This allows a Cat and a Dog to be shuffled around
fn shuffle_pair(a: &impl Animal, b: &impl Animal) {
    // 'a' could be a Cat, 'b' could be a Dog
}

// TURBOFISH: This FORCES both to be the same type
fn shuffle_pair<T: Animal>(a: &T, b: &T) {
    // a and b MUST be the same kind of animal
}

fn main() {
    let cat = Cat { name: String::from("Whiskers") };
    let dog = Dog { name: String::from("Rover") };
    
    // Works with shorthand (different types allowed)
    shuffle_pair(&cat, &dog); // OK
    
    // ERROR with turbofish (cat and dog are different types!)
    // shuffle_pair::<Cat>(&cat, &dog); // 💥 Dog ≠ Cat
}

Тип данных на выходе равен типу на входе

// You put in a specific animal, you get THAT same type back
fn clone_animal<A: Animal + Clone>(original: &A) -> A {
    original.clone() // Returns the same type that went in
}

fn main() {
    let cat = Cat { name: String::from("Whiskers") };
    let cloned_cat = clone_animal(&cat); // We know cloned_cat is a Cat
}

Нужно НЕСКОЛЬКО типажей одновременно

// This thing must be Flying AND have a BattleCry
fn dramatic_entrance<F: Flying + BattleCry>(fighter: &F) {
    fighter.battle_cry();
    fighter.fly();
}