impl Trait

impl Trait与泛型类似。你还记得,泛型使用一个类型 T(或任何其他名称),然后在程序编译时决定。首先我们来看一个具体的类型:

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);
}

这个打印:10 is higher..

但是这个只接受i32,所以现在我们要把它做成通用的。我们需要比较,我们需要用{}打印,所以我们的类型T需要PartialOrdDisplay。记住,这意味着 "只接受已经实现PartialOrdDisplay的类型"。

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);
}

现在我们来看看impl Trait,它也是类似的。我们可以引入一个类型 impl Trait,而不是 T。然后它将带入一个实现该特性的类型。这几乎是一样的。

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);
}

然而,更有趣的是,我们可以返回 impl Trait,这让我们可以返回闭包,因为它们的函数签名是trait。你可以在有它们的方法的签名中看到这一点。例如,这是 .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)的意思是,它需要两个通用类型。F是指从实现.map()的容器中取一个元素的函数,B是该函数的返回类型。然后在where之后,我们看到的是trait bound。("trait bound"的意思是 "它必须有这个trait"。)一个是Sized,接下来是闭包签名。它必须是一个 FnMut,并在 Self::Item 上做闭包,也就是你给它的迭代器。然后它返回B

所以我们可以用同样的方法来返回一个闭包。要返回一个闭包,使用 impl,然后是闭包签名。一旦你返回它,你就可以像使用一个函数一样使用它。下面是一个函数的小例子,它根据你输入的文本给出一个闭包。如果你输入 "double "或 "triple",那么它就会把它乘以2或3,否则就会返给你相同的数字。因为它是一个闭包,我们可以做任何我们想做的事情,所以我们也打印一条信息。

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);
}

下面是一个比较长的例子。让我们想象一下,在一个游戏中,你的角色面对的是晚上比较强的怪物。我们可以创建一个叫TimeOfDay的枚举来记录一天的情况。你的角色叫西蒙,有一个叫character_fear的数字,也就是f64。它晚上上升,白天下降。我们将创建一个change_fear函数,改变他的恐惧,但也做其他事情,如写消息。它大概是这样的:

enum TimeOfDay { // just a simple enum
    Dawn,
    Day,
    Sunset,
    Night,
}

fn change_fear(input: TimeOfDay) -> impl FnMut(f64) -> f64 { // The function takes a TimeOfDay. It returns a closure.
                                                             // We use impl FnMut(64) -> f64 to say that it needs to
                                                             // change the value, and also gives the same type back.
    use TimeOfDay::*; // So we only have to write Dawn, Day, Sunset, Night
                      // Instead of TimeOfDay::Dawn, TimeOfDay::Day, etc.
    match input {
        Dawn => |x| { // This is the variable character_fear that we give it later
            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; // Start Simon with 10

    let mut daytime = change_fear(Day); // Make four closures here to call every time we want to change Simon's fear.
    let mut sunset = change_fear(Sunset);
    let mut night = change_fear(Night);
    let mut morning = change_fear(Dawn);

    character_fear = daytime(character_fear); // Call the closures on Simon's fear. They give a message and change the fear number.
                                              // In real life we would have a Character struct and use it as a method instead,
                                              // like this: character_fear.daytime()
    character_fear = sunset(character_fear);
    character_fear = night(character_fear);
    character_fear = morning(character_fear);
}

这个打印:

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