0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

Rust語言中閉包的應(yīng)用場景

科技綠洲 ? 來源:TinyZ ? 作者:TinyZ ? 2023-09-20 11:25 ? 次閱讀

Rust語言的閉包是一種可以捕獲外部變量并在需要時執(zhí)行的匿名函數(shù)。閉包在Rust中是一等公民,它們可以像其他變量一樣傳遞、存儲和使用。閉包可以捕獲其定義范圍內(nèi)的變量,并在必要時訪問它們。這使得閉包在許多場景下非常有用,例如迭代器、異步編程和并發(fā)編程。

閉包與函數(shù)的區(qū)別在于,閉包可以捕獲它所定義的環(huán)境中的變量。這意味著,當閉包中使用變量時,它可以訪問該變量的值。在Rust中,閉包被設(shè)計為可以自動推斷變量的類型,因此可以方便地使用。

Rust閉包概念和python中Lambda表達式,Java的Lambda表達式很類似,可以幫助理解和應(yīng)用。

閉包的應(yīng)用場景

閉包在Rust語言中被廣泛應(yīng)于許多場景。例如,在多線程編程中,閉包可以用來定義線程任務(wù)。在Web開發(fā)中,閉包可以用來定義路由處理函數(shù)。在數(shù)據(jù)處理領(lǐng)域,閉包可以用來定義數(shù)據(jù)轉(zhuǎn)換和過濾函數(shù)等等。 下面,我們以Animal為例,演示如何使用閉包實現(xiàn)一些常見的數(shù)據(jù)處理和轉(zhuǎn)換操作。

use std::collections::HashMap;

#[derive(Debug)]
struct Animal {
    name: String,
    species: String,
    age: i32,
}


impl Animal {
    fn new(name: &str, species: &str, age: i32) - > Self {
        Animal {
            name: name.to_owned(),
            species: species.to_owned(),
            age,
        }
    }
}

impl Display for Animal {
    fn fmt(&self, f: &mut Formatter) - > Result {
        write!(f, "Animal info name {}, species:{}, age:{}", self.name, self.species, self.age)
    }
}

fn main() {
    let animals = vec![
        Animal::new("Tom", "Cat", 2),
        Animal::new("Jerry", "Mouse", 1),
        Animal::new("Spike", "Dog", 3),
    ];

    // 計算所有動物的平均年齡
    let total_age = animals.iter().map(|a| a.age).sum::< i32 >();
    let average_age = total_age as f32 / animals.len() as f32;
    println!("Average age: {:.2}", average_age);

    // 統(tǒng)計每個物種的數(shù)量
    let mut species_count = HashMap::new();
    for animal in &animals {
        let count = species_count.entry(animal.species.clone()).or_insert(0);
        *count += 1;
    }
    println!("Species count: {:?}", species_count);

    // 找出所有年齡大于2歲的動物
    let old_animals: Vec< _ > = animals.iter().filter(|a| a.age > 2).collect();
    println!("Old animals: {:?}", old_animals);

    // 將所有動物的名字轉(zhuǎn)換成大寫
    let upper_names: Vec< _ > = animals.iter().map(|a| a.name.to_uppercase()).collect(); 
    println!("Upper case names {:?}", upper_names);
}
//    輸出結(jié)果:
// Average age: 2.00
// Species count: {"Dog": 1, "Cat": 1, "Mouse": 1}
// Old animals: [Animal { name: "Spike", species: "Dog", age: 3 }]
// Upper case names ["TOM", "JERRY", "SPIKE"]

在上面的代碼中,我們定義了一個Animal結(jié)構(gòu)體,其中包含了動物的名稱、物種和年齡信息。我們使用Vec類型來存儲所有動物的信息。接下來,我們使用包對這些動物進行了一些常見的數(shù)據(jù)處理和轉(zhuǎn)換操作。

首先,我們計算了所有動物的平均年齡。我們使用iter()方法對Vec進行迭代,并使用map()方法將每個動物的年齡提取出來。然后,我們使用sum()方法將所有的年齡相加,并將其轉(zhuǎn)換為i32類型。最后,我們將總年齡除以動物數(shù)量,得到平均年齡。

接下來,我們統(tǒng)計了每個物種的數(shù)量。我們使用HashMap類型來存儲物種和數(shù)量的映射關(guān)系。我們使用entry方法獲取每個物種的數(shù)量,如果該物種不存在,則插入一個新的映射關(guān)系,并將數(shù)量初始化為0。最后,我們使用filter()方法和閉包找出了所有年齡大于2歲的動物。我們使用map()方法和閉包將所有動物的名字轉(zhuǎn)換成大寫,然后使用collect()方法將它們收集到一個新的Vec中。最后,我們使用map()方法和閉包將所有動物的名字轉(zhuǎn)換成大寫。

在上面的示例中,我們可以看到閉包的強大之處。使用閉包,我們可以輕松地對數(shù)據(jù)進行轉(zhuǎn)換和處理,而不必定義大量的函數(shù)。此外,閉包還可以捕獲外部環(huán)境中的變量,使得代碼更加靈活和可讀。

閉包的語法

包的語法形式如下:

|arg1, arg2, ...| body

其中,arg1、arg2...表示閉包參數(shù),body表示閉包函數(shù)體。閉包可以有多個參數(shù),也可以沒有參數(shù)。如果閉包沒有參數(shù),則可以省略||之間的內(nèi)容。

無參數(shù)閉包示例:

fn main() {
    let greet = || println!("Hello, World!");
    greet();
}
//    輸出結(jié)果:
//    Hello, World!

閉包的函數(shù)體可以是任意有效的Rust代碼,包括表達式、語句和控制流結(jié)構(gòu)等。在閉包中,我們可以使用外部作用域中的變量。這些變量被稱為閉包的自由變量,因為它們不是閉包參數(shù),但是在閉包中被引用了。

閉包的自由變量示例如下:

fn main() {
    let x = 3;
    let y = 5;
    //  在這里y,就是閉包的自由變量  
    let add = |a, b| a + b + y;
    
    println!("add_once_fn: {}", add(x,y));
}
//    輸出結(jié)果:
//    13

在上面的示例中,我們定義了一個閉包add,沒用指定參數(shù)的具體類型,這里是使用到了Rust語言的閉包類型推導特性,編譯器會在調(diào)用的地方進行類型推導。這里值得注意的幾點小技巧定義的閉包必須要有使用,否則編譯器缺少類型推導的上下文。當編譯器推導出一種類型后,它就會一直使用該類型,和泛型有本質(zhì)的區(qū)別。

//    1. 將上面例子的pringln!注釋掉, 相當于add閉包沒用任何引用,編譯報錯
error[E0282]: type annotations needed
  -- > src/main.rs:13:16
   |
13 |     let add = |a, b| a + b + y;
   |                ^
   |
help: consider giving this closure parameter an explicit type
   |
13 |     let add = |a: /* Type */, b| a + b + y;
   |                 ++++++++++++
//    2. 新增打印 println!("add_once_fn: {}", add(0.5,0.6));

error[E0308]: arguments to this function are incorrect
  -- > src/main.rs:16:33
   |
16 |     println!("add_once_fn: {}", add(0.5,0.6));
   |                                 ^^^ --- --- expected integer, found floating-point number
   |                                     |
   |                                     expected integer, found floating-point number
   |
note: closure defined here
  -- > src/main.rs:13:15
   |
13 |     let add = |a, b| a + b + y;
   |               ^^^^^^

閉包可以使用三種方式之一來捕獲自由變量:

  • ? move關(guān)鍵字:將自由變量移動到閉包內(nèi)部,使得閉包擁有自由變量的所有權(quán)。這意味著,一旦自由變量被移動,部作用域?qū)o法再次使用它。
  • ? &引用:使用引用來訪問自由變量。這意味著,外部作用域仍然擁有自由變量的所有權(quán),并且可以在閉包之后繼續(xù)使用它。
  • ? &mut可變引用:使用可變引用來訪問自由變量。這意味著,外部作用域仍然擁有自由變量的所有權(quán),并且可以在閉包之后繼續(xù)使用它。但是,只有一個可變引用可以存在于任意給定的時間。如果閉包中有多個可變引用,編譯器將無法通過。

下面是具有不同捕獲方式的閉包示例:

fn main() {
    let x = 10;
    let y = 20;
    
    // 使用move關(guān)鍵字捕獲自由變量
    let add = move |a:i32, b:i32| a + b + x;
    
    // 使用引用捕獲自由變量
    let sub = |a:i32, b:i32| a - b - y;
    
    // 使用可變引用捕獲自由變量
    let mut z = 30;
    let mut mul = |a:i32, b:i32| {
        z += 1;
        a * b * z
    };
    println!("add {}", add(x, y))
    println!("sub {}", sub(x, y))
    println!("mul {}", mul(x, y))
}
//    輸出結(jié)果:
// add 40
// sub -30
// mul 6200

在上面的示例中,我們定義了三個閉包:add、submul。

  • ? add使用move關(guān)鍵字捕獲了自由變量x,因此它擁有x的所有權(quán)。
  • ? sub使用引用捕獲了自由變量y,因此它只能訪問y的值,而不能修改它。
  • ? mul使用可變引用捕獲了自由變量z,因此它可以修改z的值。在這種情況下,我們需要使用mut關(guān)鍵字來聲明可變引用。

閉包的類型

在Rust語言中,閉包是一種特殊的類型,被稱為Fn、FnMutFnOnce。這些類型用于區(qū)分閉包的捕獲方式和參數(shù)類型。

  • ? Fn:表示閉包只是借用了自由變量,不會修改它們的值。這意味著,閉包可以在不擁有自由變量所有權(quán)的情況下訪問它們。
  • ? FnMut:表示閉包擁有自由變量的可變引用,并且可能會修改它們的值。這意味著,閉包必須擁有自由變量的所有權(quán),并且只能存在一個可變引用。
  • ? FnOnce:表示閉包擁有自由變量的所有權(quán),并且只能被調(diào)用一次。這意味著,閉包必須擁有自由變量的所有權(quán),并且只能在調(diào)用之后使用它們。

在閉包類型之間進行轉(zhuǎn)換是非常簡單的。只需要在閉包的參數(shù)列表中添加相應(yīng)的trait限定,即可將閉包轉(zhuǎn)換為特定的類型。例如,如果我們有一個Fn類型的閉包,但是需要將它轉(zhuǎn)換為FnMut類型,只需要在參數(shù)列表中添加mut關(guān)鍵字,如下所示:

fn main() {
    let x = 3;
    let y = 5;
    let add = |a:i32, b:i32| a + b;
    let mut add_mut = |a:i32, b:i32| {
        let result = a + b;
        println!("Result: {}", result);
        result
    };
    
    let add_fn: fn(i32, i32) - > i32 = add;
    let add_mut_fn: fn(i32, i32) - > i32 = add_mut;
    let add_once_fn: fn(i32, i32) - > i32 = |a:i32, b:i32| a + b + 10;
    println!("add_fn: {}", add_fn(x,y));
    println!("add_mut_fn: {}", add_mut_fn(x,y));
    println!("add_once_fn: {}", add_once_fn(x,y));
}
//    輸出結(jié)果:
// add_fn: 8
// Result: 8
// add_mut_fn: 8
// add_once_fn: 18

在上面的示例中,我們定義了三個閉包:addadd_mutadd_once。addadd_mut都是Fn類型的閉包,但是add_mut使用了可變引用,因此它也是FnMut類型閉包。我們使用fn關(guān)鍵字將閉包轉(zhuǎn)換為函數(shù)類型,并指定參數(shù)和返回值的類型。在這種情況下,我們使用i32作為參數(shù)和返回值的類型。

閉包的應(yīng)用與實踐

閉包在Rust語言中廣泛應(yīng)用于函數(shù)式編程、迭代器和多線程等領(lǐng)域。在函數(shù)式編程中,閉包常常用于實現(xiàn)高階函數(shù),如map()、filter()reduce()等。這些函數(shù)可以接受一個閉包作為參數(shù),然后對集合中的每個元素進行轉(zhuǎn)換、過濾和歸約等操作。

以下是一個使用閉包實現(xiàn)map()filter()函數(shù)的示例:

fn map< T, F >(source: Vec< T >, mut f: F) - > Vec >
where
    F:Mut(T) - > T,
{
    let mut result = Vec::new();
    for item in source {
        result.push(f(item));
    }
    result
}

fn filter< T, F >(source: Vec< T >, mut f: F) - > Vec< T >
where
    F: FnMut(&T) - > bool,
{
    let mut result = Vec::new();
    for item in source {
        if f(&item) {
            result.push(item);
        }
    }
    result
}

在上面的示例中,我們定義了map()filter()函數(shù),它們接受一個閉包作為參數(shù),并對集合中的每個元素進行轉(zhuǎn)換和過濾操作。map()函數(shù)將集合中的每個元素傳遞給閉包進行轉(zhuǎn)換,并將轉(zhuǎn)換后的結(jié)果收集到一個新的Vec中。filter()函數(shù)將集合中的每個元素傳遞給閉包進行過濾,并將通過過濾的元素收集到一個新的Vec中。

以下是一個使用閉包實現(xiàn)多線程的示例:

use std::thread;

fn main() {
    let mut handles = Vec::new();
    for i in 0..10 {
        let handle = thread::spawn(move || {
            println!("Thread {}: Hello, world!", i);
        });
        handles.push(handle);
    }
    for handle in handles {
        handle.join().unwrap();
    }
}
//    輸出結(jié)果:
// Thread 1: Hello, world!
// Thread 7: Hello, world!
// Thread 8: Hello, world!
// Thread 9: Hello, world!
// Thread 6: Hello, world!
// Thread 5: Hello, world!
// Thread 4: Hello, world!
// Thread 3: Hello, world!
// Thread 2: Hello, world!
// Thread 0: Hello, world!

在上面的示例中,我們使用thread::spawn()函數(shù)創(chuàng)建了10個新線程,并使用閉包將每個線程的編號傳遞給它們。在閉包中,我們使用move關(guān)鍵字將i移動到閉包內(nèi)部,以便在創(chuàng)建線程之后,i的所有權(quán)被轉(zhuǎn)移 給了閉包。然后,我們將每個線程的句柄存儲在一個Vec中,并使用join()函數(shù)等待每個線程完成。

總結(jié)

Rust語言中的閉包是一種非常強大的特性,可以用于實現(xiàn)高階函數(shù)、函數(shù)式編程、迭代器和多線程等領(lǐng)域。閉包具有捕獲自由變量的能力,并且可以在閉包后繼續(xù)使用它們。在Rust語言中,閉包是一種特殊的類型,被稱為Fn、FnMutOnce,用于區(qū)閉包的捕獲方式和參數(shù)類型。閉包可以通過實現(xiàn)這些trait來進行類型轉(zhuǎn)換。

盡管閉包在Rust語言中非常強大和靈活,但是使用它們時需要謹慎。閉包的捕獲方式和參數(shù)類型可能會導致所有權(quán)和可變性的問題,尤其是在多線程環(huán)境中。因此,我們應(yīng)該在使用閉包時仔細思考,并遵循Rust語言的所有權(quán)和可變性規(guī)則。

總之,閉包是一種非常有用的特性,可以幫助我們編寫更加靈活和高效的代碼。如果您還沒有使用過閉包,請嘗試在您的項目中使用它們,并體驗閉包帶來的便利和效率。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學習之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • 存儲
    +關(guān)注

    關(guān)注

    13

    文章

    4170

    瀏覽量

    85482
  • 編程
    +關(guān)注

    關(guān)注

    88

    文章

    3543

    瀏覽量

    93468
  • 數(shù)據(jù)處理
    +關(guān)注

    關(guān)注

    0

    文章

    554

    瀏覽量

    28483
  • rust語言
    +關(guān)注

    關(guān)注

    0

    文章

    57

    瀏覽量

    2993
  • 閉包
    +關(guān)注

    關(guān)注

    0

    文章

    4

    瀏覽量

    2046
收藏 人收藏

    評論

    相關(guān)推薦

    聊聊Rust與C語言交互的具體步驟

    rust FFI 是rust與其他語言互調(diào)的橋梁,通過FFI rust 可以有效繼承 C 語言的歷史資產(chǎn)。本期通過幾個例子來聊聊
    發(fā)表于 07-06 11:15 ?1571次閱讀

    C語言中預(yù)定義宏的用法和使用場景

    在C語言中,預(yù)定義宏是由編譯器提供的一組特殊標識符,可以在程序中直接使用,無需進行額外的定義。
    發(fā)表于 08-16 16:12 ?424次閱讀

    如何在Rust中高效地操作文件

    Rust語言是一種系統(tǒng)級、高性能的編程語言,其設(shè)計目標是確保安全和并發(fā)性。 Rust語言以C和C++為基礎(chǔ),但是對于安全性和并發(fā)性做出了很大
    的頭像 發(fā)表于 09-19 11:51 ?2192次閱讀

    SQLx在Rust語言中的基礎(chǔ)用法和進階用法

    SQLx是一個Rust語言的異步SQL執(zhí)行庫,它支持多種數(shù)據(jù)庫,包括MySQL、PostgreSQL、SQLite等。本教程將以MySQL數(shù)據(jù)庫為例,介紹SQLx在Rust語言中的基礎(chǔ)
    的頭像 發(fā)表于 09-19 14:32 ?4803次閱讀

    Rust語言中錯誤處理的機制

    Rust語言中,錯誤處理是一項非常重要的任務(wù)。由于Rust語言采用靜態(tài)類型檢查,在編譯時就能發(fā)現(xiàn)很多潛在的錯誤,這使得程序員能夠更加自信和高效地開發(fā)程序。然而,即使我們在編譯時盡可能
    的頭像 發(fā)表于 09-19 14:54 ?1285次閱讀

    基于Rust語言Hash特征的基礎(chǔ)用法和進階用法

    Rust語言是一種系統(tǒng)級編程語言,具有高性能、安全、并發(fā)等特點,是近年來備受關(guān)注的新興編程語言。在Rust
    的頭像 發(fā)表于 09-19 16:02 ?1312次閱讀

    Rust語言中的反射機制

    Rust語言的反射機制指的是在程序運行時獲取類型信息、變量信息等的能力。Rust語言中的反射機制主要通過 Any 實現(xiàn)。 std::any::Any trait Any trait是所
    的頭像 發(fā)表于 09-19 16:11 ?2139次閱讀

    Rust語言如何與 InfluxDB 集成

    Rust 是一種系統(tǒng)級編程語言,具有高性能和內(nèi)存安全性。InfluxDB 是一個開源的時間序列數(shù)據(jù)庫,用于存儲、查詢和可視化大規(guī)模數(shù)據(jù)集。Rust 語言可以與 InfluxDB 集成,
    的頭像 發(fā)表于 09-30 16:45 ?1026次閱讀

    基于Rust語言中的生命周期

    Rust是一門系統(tǒng)級編程語言具備高效、安和并發(fā)等特,而生命周期是這門語言中比較重要的概念之一。在這篇教程中,我們會了解什么是命周期、為什么需要生命周期、如何使用生命周期,同時我們依然會使用老朋友
    的頭像 發(fā)表于 09-19 17:03 ?830次閱讀

    Rust 語言中的 RwLock內(nèi)部實現(xiàn)原理

    Rust是一種系統(tǒng)級編程語言,它帶有嚴格的內(nèi)存管理、并發(fā)和安全性規(guī)則,因此很受廣大程序員的青睞。RwLock(讀寫鎖)是 Rust 中常用的線程同步機制之一,本文將詳細介紹 Rust
    的頭像 發(fā)表于 09-20 11:23 ?771次閱讀

    用最簡單的語言解釋Python的是什么?

    很藍瘦,你應(yīng)該盡量理解一下它。
    的頭像 發(fā)表于 03-21 16:33 ?2067次閱讀

    詳細介紹go語言中的實現(xiàn)

    什么是? 什么場景下會用 ? 本文對 go 語言中
    的頭像 發(fā)表于 10-20 16:18 ?1814次閱讀

    帶你了解go語言中

    ? 【 導讀】什么是? 什么場景下會用 ? 本文對 go 語言中
    的頭像 發(fā)表于 11-02 15:27 ?2378次閱讀

    谷歌開源內(nèi)部Rust Crate審計結(jié)果

    Rust 可以輕松地將代碼封裝和共享到 crate 中,crate 是可重用的軟件組件,就像其他語言中一樣。我們擁抱廣泛的開源 Rust crate 生態(tài)系統(tǒng),既利用了谷歌以外編
    的頭像 發(fā)表于 05-29 11:10 ?752次閱讀

    tokio模塊channel中的使用場景和優(yōu)缺點

    以讓不同的線程之間通過發(fā)送和接收消息來傳遞數(shù)據(jù),從而實現(xiàn)線程之間的協(xié)作和同步。 在 Rust 語言中,tokio 模塊的 channel 組件提供了
    的頭像 發(fā)表于 09-19 15:54 ?688次閱讀