impl 特徵
impl 特徵
與泛型類似。你還記得,泛型使用型別 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 需要具有 PartialOrd
和 Display
特徵。記住,這意味著"只接受已經具有 PartialOrd
和 Display
的型別"。
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 特徵
。我們可以帶入 impl 特徵
型別,而不是 T
型別。然後它將接受實作該特徵的型別。這幾乎是一樣的:
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 特徵
,這讓我們可以回傳閉包,因為它們的函式簽名是特徵。你可以在有使用它們的方法的簽名中見到這點。例如,這是 .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)。("特徵界限"的意思是"它必須有這個特徵"。)一個是 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; // 做出三個閉包 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 { // 只是單純的列舉 Dawn, Day, Sunset, Night, } fn change_fear(input: TimeOfDay) -> impl FnMut(f64) -> f64 { // 這個函式接受 TimeOfDay. 回傳閉包. // 我們用 impl FnMut(64) -> 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; // 西蒙從 10 開始 let mut daytime = change_fear(Day); // 這裡做四個閉包在每次我們想改變西蒙的恐懼時去呼叫. let mut sunset = change_fear(Sunset); let mut night = change_fear(Night); let mut morning = change_fear(Dawn); character_fear = daytime(character_fear); // 對西蒙的恐懼呼叫閉包. 它們給出訊息並改變恐懼數值. // 在現實生活我們會有 Character 結構體並把它當方法用, // 像這樣: 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