Rust 函數(shù)式編程的具體使用
Rust 是一門多范式語(yǔ)言,既可以像 C++/Java 那樣寫“命令式代碼”,也支持“函數(shù)式編程”。但很多剛?cè)腴T的小伙伴可能會(huì)有這些疑問(wèn):
函數(shù)不就是函數(shù)嗎?什么是純函數(shù)?
什么又是副作用?函數(shù)式和我熟悉的 Java/C++ 有啥區(qū)別?
我該怎么開(kāi)始寫函數(shù)式風(fēng)格的 Rust 代碼?
別急,今天我們從零出發(fā),把這些看起來(lái)很抽象的概念通通講清楚!
一、什么是函數(shù)式編程?一句話概括
函數(shù)式編程(Functional Programming)是:用純粹、可組合的函數(shù)來(lái)表達(dá)程序邏輯,同時(shí)避免副作用。
看不懂?我們來(lái)一句一句拆開(kāi)講 ??
1. 什么是“純粹的函數(shù)”(純函數(shù))?
很多人第一反應(yīng)是:“函數(shù)不都是函數(shù)嗎?為啥還要強(qiáng)調(diào)‘純’?”
來(lái),舉個(gè)例子你就懂了:
舉個(gè)例子
fn add(a: i32, b: i32) -> i32 { a + b }
這個(gè)函數(shù):
- 輸入什么,輸出就是什么(比如 2 + 3 永遠(yuǎn)等于 5)
- 不會(huì)打印東西、不寫文件、不改全局變量
? 所以它是個(gè)“純函數(shù)”。
再舉一個(gè)反面例子
fn print_and_add(a: i32, b: i32) -> i32 { println!("正在加法運(yùn)算!"); a + b }
這個(gè)函數(shù)除了計(jì)算結(jié)果,還打印了一句話,這叫做副作用。
2. 什么是“副作用”?
副作用 = 函數(shù)除了返回結(jié)果,還影響了“外部世界”。
行為 | 是副作用嗎? | 原因 |
---|---|---|
改了一個(gè)全局變量 | ? 是 | 改變了外部狀態(tài) |
打印輸出 println! | ? 是 | 改變了控制臺(tái) |
寫入文件 | ? 是 | 改變了磁盤狀態(tài) |
發(fā) HTTP 請(qǐng)求 | ? 是 | 影響了外部網(wǎng)絡(luò) |
單純返回值 | ? 否 | 沒(méi)動(dòng)外部任何東西 |
為什么函數(shù)式編程追求“無(wú)副作用”?
因?yàn)楦弊饔茫?/p>
- 會(huì)讓程序變得難以預(yù)測(cè)(打印/寫文件在哪影響了誰(shuí)?)
- 不利于并發(fā)(多個(gè)線程操作全局變量可能會(huì)打架)
- 不好測(cè)試(一個(gè)函數(shù)打印日志、改配置很難自動(dòng)驗(yàn)證)
3. 什么是“組合性”?為啥函數(shù)式編程要“可組合”?
組合性 = 把小函數(shù)像積木一樣拼起來(lái),組成更大的邏輯
比如:
let data = vec![1, 2, 3, 4, 5]; let result: i32 = data .iter() // 遍歷 .filter(|x| *x % 2 == 0) // 只保留偶數(shù) .map(|x| x * 2) // 每個(gè)數(shù)翻倍 .sum(); // 求和 println!("{}", result); // 輸出 12(2*2 + 4*2)
這段代碼沒(méi)有循環(huán)、沒(méi)有中間變量,卻能一步步地處理數(shù)據(jù)。
每個(gè)函數(shù)(如 filter
, map
)都很簡(jiǎn)單,但組合起來(lái)就完成了復(fù)雜的邏輯!
這種“拼積木”的能力,就是組合性。
函數(shù)式編程的三大思想總結(jié):
概念 | 通俗解釋 | 關(guān)鍵目的 |
---|---|---|
純函數(shù) | 不依賴外部,不改外部,只靠輸入決定輸出 | 穩(wěn)定、可預(yù)測(cè) |
無(wú)副作用 | 不打印、不改文件、不改全局變量 | 可測(cè)試、線程安全 |
可組合 | 把小函數(shù)組合成大邏輯 | 簡(jiǎn)潔、模塊化 |
函數(shù)式 VS 命令式(C++/Java)
對(duì)比點(diǎn) | 命令式(C++/Java) | 函數(shù)式(Rust風(fēng)格) |
---|---|---|
編程方式 | 寫“怎么做” | 寫“要什么” |
控制結(jié)構(gòu) | for、if、變量改來(lái)改去 | map/filter/鏈?zhǔn)教幚?/td> |
狀態(tài)管理 | 變量經(jīng)常變化 | 默認(rèn)不可變 |
副作用 | 難避免 | 盡量消除 |
函數(shù)角色 | 封裝邏輯 | 構(gòu)建模塊 |
可讀性 | 操作細(xì)節(jié)多 | 更像自然語(yǔ)言表達(dá) |
Rust 支持函數(shù)式編程的方式(顯著特征表)
特征 | Rust 中的支持方式 | 示例 |
---|---|---|
? 純函數(shù) | 所有普通函數(shù)默認(rèn)都可以寫成純函數(shù) | fn add(a, b) -> a + b |
? 不可變性 | 默認(rèn) let 是不可變的 | let x = 5; |
? 閉包(匿名函數(shù)) | 使用 |x| x + 1 定義 | let f = |x| x + 1; |
? 高階函數(shù) | 函數(shù)可以作為參數(shù)傳入 | map(|x| x * 2) |
? 惰性計(jì)算 | Iterator 是惰性執(zhí)行的 | .iter().map().filter() |
? 函數(shù)組合 | 使用鏈?zhǔn)秸{(diào)用 | .map().filter().sum() |
?? 解釋閉包:閉包就是一個(gè)沒(méi)有名字的“臨時(shí)函數(shù)”,可以捕獲外部變量,語(yǔ)法是 |參數(shù)| 表達(dá)式
。
如何從零開(kāi)始上手 Rust 函數(shù)式編程?
很多人卡在一開(kāi)始不知道怎么寫函數(shù)式代碼,我們一步步來(lái):
第一步:掌握函數(shù)式寫法格式
寫法 | 示例 | 含義 |
---|---|---|
匿名函數(shù)(閉包) | 使用 |x| x + 1 定義 | let f = |x| x + 1; |
高階函數(shù) | map(|x| x * 2) | 傳函數(shù)給函數(shù) |
鏈?zhǔn)秸{(diào)用 | .filter().map() | 像流水線一樣處理數(shù)據(jù) |
collect 收集結(jié)果 | .collect::<Vec<_>>() | 把處理結(jié)果收集成 Vec |
第二步:從 for 循環(huán)重構(gòu)開(kāi)始
傳統(tǒng)寫法:
let mut result = vec![]; for i in 1..=5 { if i % 2 == 0 { result.push(i * 2); } }
函數(shù)式寫法:
let result: Vec<_> = (1..=5) .filter(|x| x % 2 == 0) .map(|x| x * 2) .collect();
第三步:試著傳函數(shù)給函數(shù)(高階函數(shù))
fn operate(x: i32, f: fn(i32) -> i32) -> i32 { f(x) } fn main() { let double = |x| x * 2; println!("{}", operate(3, double)); // 輸出 6 }
總結(jié):Rust 函數(shù)式編程,到底有什么價(jià)值?
優(yōu)點(diǎn) | 對(duì)初學(xué)者的意義 |
---|---|
? 代碼更短更清晰 | 不需要手動(dòng)管理中間變量 |
? 更容易測(cè)試 | 沒(méi)副作用就是好測(cè)試 |
? 更少 bug | 不容易改錯(cuò)變量 |
? 更好并發(fā)支持 | 不爭(zhēng)搶變量,天然線程安全 |
后續(xù)你可以這樣學(xué)習(xí)函數(shù)式思維:
- 把所有
for
循環(huán)都試著用.iter().map().filter()
改寫 - 學(xué)會(huì)閉包、理解閉包和變量捕獲
- 閱讀標(biāo)準(zhǔn)庫(kù)
Iterator
Trait 的文檔 - 多寫鏈?zhǔn)浇M合:map、filter、fold、collect
- 理解
Option
/Result
和函數(shù)式結(jié)合的優(yōu)雅用法
到此這篇關(guān)于Rust 函數(shù)式編程的具體使用的文章就介紹到這了,更多相關(guān)Rust 函數(shù)式編程內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Rust 中的 Packages 與 Crates模塊化構(gòu)建的基礎(chǔ)及開(kāi)發(fā)流程
Rust中的Crate是編譯器處理的最小代碼單元,可以是二進(jìn)制或庫(kù),每個(gè)Crate由一個(gè)CrateRoot文件(通常是src/main.rs或src/lib.rs)定義,本文給大家介紹Rust 中的 Packages 與 Crates模塊化構(gòu)建的基礎(chǔ)及開(kāi)發(fā)流程,感興趣的朋友一起看看吧2025-02-02用rust?寫一個(gè)jar包?class沖突檢測(cè)工具
這篇文章主要介紹了用rust?寫一個(gè)jar包?class沖突檢測(cè)工具?的相關(guān)資料,需要的朋友可以參考下2023-05-05Rust動(dòng)態(tài)數(shù)組Vec基本概念及用法
Rust中的Vec是一種動(dòng)態(tài)數(shù)組,它可以在運(yùn)行時(shí)自動(dòng)調(diào)整大小,本文主要介紹了Rust動(dòng)態(tài)數(shù)組Vec基本概念及用法,具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12Rust生命周期之驗(yàn)證引用有效性與防止懸垂引用方式
本文介紹了Rust中生命周期注解的應(yīng)用,包括防止懸垂引用、在函數(shù)中使用泛型生命周期、生命周期省略規(guī)則、在結(jié)構(gòu)體中使用生命周期、靜態(tài)生命周期以及如何將生命周期與泛型和特質(zhì)約束結(jié)合,通過(guò)這些機(jī)制,Rust在編譯時(shí)就能捕獲內(nèi)存安全問(wèn)題2025-02-02Rust中的Box<T>之堆上的數(shù)據(jù)與遞歸類型詳解
本文介紹了Rust中的Box<T>類型,包括其在堆與棧之間的內(nèi)存分配,性能優(yōu)勢(shì),以及如何利用Box<T>來(lái)實(shí)現(xiàn)遞歸類型和處理大小未知類型,通過(guò)Box<T>,Rust程序員可以更靈活地管理內(nèi)存,避免編譯時(shí)大小不確定的問(wèn)題,并提高代碼的效率和靈活性2025-02-02rust程序靜態(tài)編譯的兩種方法實(shí)例小結(jié)
這篇文章主要介紹了rust程序靜態(tài)編譯的兩種方法總結(jié),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2025-05-05