13.2.0. 寫在正文之前
Rust語言在設計過程中收到了很多語言的啟發,而函數式編程對Rust產生了非常顯著的影響。函數式編程通常包括通過將函數作為值傳遞給參數、從其他函數返回它們、將它們分配給變量以供以后執行等等。
在本章中,我們會討論 Rust 的一些特性,這些特性與許多語言中通常稱為函數式的特性相似:
- 閉包(本文)
- 迭代器
- 使用閉包和迭代器改進I/O項目
- 閉包和迭代器的性能
喜歡的話別忘了點贊、收藏加關注哦(加關注即可閱讀全文),對接下來的教程有興趣的可以關注專欄。謝謝喵!(=・ω・=)
13.2.1. 閉包的類型推斷
和fn
定義的函數不同,閉包不強制要求標注參數和返回值的類型。
函數需要強制標注是因為它是暴露給用戶的顯示接口的一部分,嚴格定義接口有助于所有人對參數和返回值的類型取得共識。
閉包并不會被用于這樣的暴露接口,只會被存于變量中,使用時也不需要命名,更不會被暴露給我們代碼庫的用戶。所以,閉包不強制要求標注參數和返回值的類型。
而且閉包通常很短小,只在狹小的上下文中工作,編譯器通常能推斷出類型。當然你手動標注出來也不是不可以。
看個例子:
這是使用函數定義的代碼:
fn simulated_expensive_calculation(intensity: u32) -> u32 { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); intensity
}
這是使用閉包的代碼:
let expensive_closure = |num:u32| -> u32 { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); num
};
這里使用顯式標注是因為沒有前后文供Rust推斷類型,如果有,就不需要:
fn generate_workout(intensity: u32, random_number: u32) { let expensive_closure = |num| { println!("calculating slowly..."); thread::sleep(Duration::from_secs(2)); num }; if intensity < 25 { println!("Today, do {} pushups!", expensive_closure(intensity)); println!("Next, do {} situps!", expensive_closure(intensity)); } else { if random_number == 3 { println!("Take a break today! Remember to stay hydrated!"); } else { println!("Today, run for {} minutes!", expensive_closure(intensity)); } }
}
這里的參數num
不需要顯式聲明類型是因為下文的調用中傳進去的參數intensity
的類型為u32
,Rust推斷出num
的類型為u32
。
13.2.2. 函數和閉包定義的語法
這里有4個例子:
fn add_one_v1 (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x| { x + 1 };
let add_one_v4 = |x| x + 1 ;
- 第一個是函數的定義,有函數名,形參名及類型和返回值類型
- 第二個是閉包的定義,有參數和返回值的類型。這個閉包看著和函數的定義差不多。
- 第三個同樣是閉包,但是沒有標注參數和返回值的類型,就得靠編譯器推斷了。
- 第四個閉包跟第三個的不同之處在于沒有了花括號
{}
。因為只有一個表達式,所以閉包的{}
也可以被省略
13.2.3. 閉包的類型推斷
閉包的定義最終只會為參數/返回值推斷出唯一具體的類型。
看個例子:
let example_closure = |x| x;let s = example_closure(String::from("hello"));let n = example_closure(5);
輸出:
$ cargo runCompiling closure-example v0.1.0 (file:///projects/closure-example)
error[E0308]: mismatched types--> src/main.rs:5:29|
5 | let n = example_closure(5);| --------------- ^- help: try using a conversion method: `.to_string()`| | || | expected `String`, found integer| arguments to this function are incorrect|
note: expected because the closure was earlier called with an argument of type `String`--> src/main.rs:4:29|
4 | let s = example_closure(String::from("hello"));| --------------- ^^^^^^^^^^^^^^^^^^^^^ expected because this argument is of type `String`| || in this closure call
note: closure parameter defined here--> src/main.rs:2:28|
2 | let example_closure = |x| x;| ^For more information about this error, try `rustc --explain E0308`.
error: could not compile `closure-example` (bin "closure-example") due to 1 previous error
Rust編譯器在閉包第一次被調用時發現它接收的值和輸出的值都是String
類型,就鎖定這個閉包的參數和返回值都是String
類型。所以后面又使用i32
類型時就會報錯。