49-impl Trait
impl Trait — Easy Rust 中文译本
译文 · 基于 Easy Rust
原文链接: https://dhghomon.github.io/easy_rust/Chapter_48.html
中文参考:kumakichi/easy_rust_chs
impl Trait
impl Trait与泛型类似。你还记得,泛型使用一个类型 T(或任何其他名称),然后在程序编译时决定。首先我们来看一个具体的类型:
1
2
3
4
5
6
7
8
| 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需要PartialOrd和Display。记住,这意味着 “只接受已经实现PartialOrd和Display的类型”。
1
2
3
4
5
6
7
8
9
10
| 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。然后它将带入一个实现该特性的类型。这几乎是一样的。
1
2
3
4
5
6
7
8
9
10
| fn prints_it(input: impl Into<String> + std::fmt::Display) { // 接受任何能转成 String 且实现 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() 的签名。
1
2
3
4
5
6
7
| 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,否则就会返给你相同的数字。因为它是一个闭包,我们可以做任何我们想做的事情,所以我们也打印一条信息。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
| 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;
// 创建三个闭包
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函数,改变他的恐惧,但也做其他事情,如写消息。它大概是这样的:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
| enum TimeOfDay { // 只是一个简单枚举
Dawn,
Day,
Sunset,
Night,
}
fn change_fear(input: TimeOfDay) -> impl FnMut(f64) -> f64 { // 函数接受 TimeOfDay,返回一个闭包。
// 用 impl FnMut(f64) -> f64 表示它需要
// 改变值,并返回相同类型。
use TimeOfDay::*; // 这样就只需写 Dawn、Day、Sunset、Night
// 而不必写 TimeOfDay::Dawn、TimeOfDay::Day 等。
match input {
Dawn => |x| { // 这就是我们稍后传入的 character_fear 变量
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; // 给 Simon 初始恐惧值 10
let mut daytime = change_fear(Day); // 在这里创建四个闭包,每次想改 Simon 的恐惧时调用。
let mut sunset = change_fear(Sunset);
let mut night = change_fear(Night);
let mut morning = change_fear(Dawn);
character_fear = daytime(character_fear); // 对 Simon 的恐惧调用闭包。它们会打印消息并改变恐惧值。
// 实际项目里我们会有 Character 结构体,并把它做成方法,
// 像这样:character_fear.daytime()
character_fear = sunset(character_fear);
character_fear = night(character_fear);
character_fear = morning(character_fear);
}
|
这个打印:
1
2
3
4
5
6
7
8
| 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
|