Teknologian jakaminen

Ruosteohjelmointikielen oppiminen - toiminnalliset kielen ominaisuudet: iteraattorit ja sulkemiset

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

Ruoste Sulkeminen on anonyymi funktio, joka voidaan tallentaa muuttujaan tai välittää argumenttina muille funktioille. Voit luoda sulkemisen yhteen paikkaan ja suorittaa sitten sulkemistoiminnot eri kontekstissa. Toisin kuin funktiot, sulkemiset mahdollistavat arvojen kaappaamisen niiden määrittelyalueen sisällä.

Iteraattori on vastuussa logiikasta, joka kulkee sekvenssin jokaisen kohteen läpi ja määrittää, milloin sekvenssi päättyy. Iteraattoreita käytettäessä meidän ei tarvitse toteuttaa tätä logiikkaa uudelleen.

1. Sulkeminen

Sulkeminen, funktiomainen rakenne, joka voidaan tallentaa muuttujaan.

1.1 Sulkemiset vangitsevat ympäristönsä

Rustissa sulkemiset ovat anonyymejä toimintoja, jotka sieppaavat muuttujia ulkoisesta ympäristöstä. Sulkemisen sieppauskäyttäytyminen riippuu muuttujien tyypeistä ja siitä, kuinka sulkeminen käyttää näitä muuttujia.

Tallenna arvon mukaan (arvon mukaan)

Kun sulkeminen kaappaa muuttujan arvon perusteella, se ottaa muuttujan omistukseensa. Tämä tarkoittaa, että sulkemisen luomisen jälkeen alkuperäistä muuttujaa ei voi enää käyttää.

fn main() {
    let text = "Hello".to_string();
    // 使用 move 来显式地表示闭包将获取 text 的所有权
    let closure = move || println!("{}", text);
    // 这里 text 不能被使用,因为其所有权已经被闭包获取
    // println!("{}", text); // 这将导致编译错误
    closure(); // 打印 "Hello"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Kaappaa viitteellä (viittauksella)

Kun sulkeminen kaappaa muuttujan viittauksella, se lainaa tämän muuttujan. Tämä tarkoittaa, että alkuperäinen muuttuja on edelleen saatavilla, mutta sulkeja voi vain lainata sitä, ei ottaa omistukseensa.

fn main() {
    let text = "Hello";
    // 闭包通过引用捕获 text
    let closure = || println!("{}", text);
    // text 仍然可用,因为它没有被移动
    println!("{}", text); // 打印 "Hello"
    closure(); // 再次打印 "Hello"
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Muuttuva sieppaus

Sulkeminen voi siepata muuttuvan viittauksen, jolloin se voi muokata alkuperäisen muuttujan arvoa.

fn main() {
    let mut count = 0;
    // 闭包通过可变引用捕获 count
    let mut closure = || {
        count += 1; // 修改 count 的值
        println!("Count: {}", count);
    };
    closure(); // 打印 "Count: 1"
    closure(); // 打印 "Count: 2"
    // count 的值现在是 2
    println!("Final count: {}", count);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Jos suljin poistetaan mut Muokkaus, kokoaminen epäonnistuu.

fn main() {
    let mut count = 0;
    // 闭包通过可变引用捕获 count
    let closure = || {
        count += 1; // 修改 count 的值
        println!("Count: {}", count);
    };
    closure(); // 打印 "Count: 1"
    closure(); // 打印 "Count: 2"
    // count 的值现在是 2
    println!("Final count: {}", count);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

Virheviestikehotteet: Johtuu muuttujan lainaamisesta count,siirtää closure Vaihtuva sidonta vaaditaan.

   Compiling playground v0.0.1 (/playground)
error[E0596]: cannot borrow `closure` as mutable, as it is not declared as mutable
 --> src/main.rs:4:9
  |
4 |     let closure = || {
  |         ^^^^^^^ not mutable
5 |         count += 1; // 修改 count 的值
  |         ----- calling `closure` requires mutable binding due to mutable borrow of `count`
...
8 |     closure(); // 打印 "Count: 1"
  |     ------- cannot borrow as mutable
9 |     closure(); // 打印 "Count: 2"
  |     ------- cannot borrow as mutable
  |
help: consider changing this to be mutable
  |
4 |     let mut closure = || {
  |         +++

For more information about this error, try `rustc --explain E0596`.
error: could not compile `playground` (bin "playground") due to 1 previous error
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

sulkeminen argumenttina

Sulkimia voidaan käyttää mmParametrien välitysMuihin toimintoihin kaappaa muuttujat ympäristössä.

fn main() {
    // 创建一个整数变量
    let number = 10;

    // 创建一个闭包,它接受一个 i32 类型的参数并返回其平方
    // 这里使用 || 表示这是一个闭包
    let square = || number * number;

    // 定义一个函数,它接受一个闭包作为参数并调用它
    // 闭包作为参数需要指定其类型,这里使用 || -> i32 表示闭包没有参数并返回 i32 类型的值
    fn call_closure<F>(f: F)
    where
        F: Fn() -> i32, // 使用 trait bound 指定闭包的签名
    {
        // 调用闭包并打印结果
        let result = f();
        println!("The result is: {}", result);
    }

    // 调用 `call_closure` 函数,并将闭包 `square` 作为参数传递
    // 由于闭包 `square` 没有参数,我们可以直接传递
    call_closure(square);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

Tässä esimerkissä:

  1. Määrittelemme sulkemisen square, joka kaappaa viittauksella main toiminnassanumber muuttuja ja laske sen neliö.
  2. Määrittelemme tiedoston nimeltä call_closure funktio, joka hyväksyy aFn() -> i32 Piirteeseen sidotut sulkemiset ottavat parametreja, mikä tarkoittaa, että sulkeminen ei ota parametreja ja palauttaa ai32 arvon tyyppi.
  3. Kutsumme call_closure toimintaa ja tahtoasquare Sulkemiset välitetään argumentteina.koskasquare Sulkuilla ei ole parametreja, ne voidaan välittää suoraan parametreinacall_closure
  4. call_closure Funktio kutsuu sulkemisen ja tulostaa tuloksen.

1.2 Sulkemistyypin päättely ja huomautukset

Toimintojen ja sulkemisten välillä on paljon enemmän eroja.Sulkeminen ei aina vaadi jotain sellaistafn Määritä parametrien tyypit ja palautusarvot, kuten funktiot. Tyyppimerkinnät ovat pakollisia funktioissa, koska ne ovat osa käyttäjän nähtävää eksplisiittistä käyttöliittymää. On tärkeää määritellä nämä rajapinnat tarkasti, jotta kaikilla on johdonmukainen käsitys käytetyistä funktioista ja palautusarvotyypeistä. Sitä vastoin tällaisissa avoimessa rajapinnassa ei käytetä sulkuja: ne tallennetaan muuttujiin ja niitä käytetään nimeämättä tai paljastamatta niitä kirjaston käyttäjille.

Sulkemiset ovat yleensä lyhyitä ja liittyvät kapeaan kontekstiin pikemminkin kuin mielivaltaiseen tilanteeseen. Näissä rajoitetuissa yhteyksissä kääntäjä voi luotettavasti päätellä parametrien tyypit ja palauttaa arvot samalla tavalla kuin useimpien muuttujien tyypit (vaikka on harvinaisia ​​tapauksia, joissa kääntäjä vaatii sulkemistyyppisiä huomautuksia).

Muuttujien tapaan tyyppimerkintöjä voidaan lisätä, jos haluamme lisätä selkeyttä ja selkeyttä, ja haittapuolena on tehdä koodista monisanaisempi (toisin kuin ehdottoman välttämätön).

fn main() {
    let a = 100;
    let add_one = |x: i32| -> i32 { x + 1 };
    let b = add_one(a);
    println!("{}", b); 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Tyyppimerkintöjen kanssa sulkemisten syntaksi muistuttaa enemmän funktioita. Tässä on vertikaalinen vertailu funktion määritelmästä, joka lisää sen argumenttiin, ja sulkemissyntaksia, jolla on sama toiminta. Tähän on lisätty välilyöntejä vastaavien osien kohdistamiseksi. Tämä näyttää, kuinka samanlainen sulkemissyntaksi toimii syntaksin kanssa, lukuun ottamatta putkien käyttöä ja joitain valinnaisia ​​syntaksia:

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  ;
  • 1
  • 2
  • 3
  • 4

Joten yllä oleva esimerkki voidaan yksinkertaistaa seuraavasti:

fn main() {
    let a = 100;
    let add_one = |x| x + 1;
    let b = add_one(a);
    println!("{}", b); 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Kääntäjä päättelee kunkin parametrin konkreettisen tyypin ja palautusarvon sulkemismääritelmässä.

Katsotaanpa toista esimerkkiä:

fn main() {
    let a = 100i32;
    let a1 = 100f32;
    let closure = |x| x;
    let b = closure(a);
    let b1 = closure(a1);
    println!("{}", b); 
    println!("{}", b1); 
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Huomaa, että tämä sulkemisen määritelmä ei lisää tyyppimerkintöjä, joten voimme kutsua tätä sulkemista millä tahansa tyypillä. Mutta jos yrität kutsua sulkemista kahdesti, ensimmäisen kerran i32:lla ja toisen kerran käyttämällä f32:ta, raportoidaan virhe:Odotettu, koska sulkeminen kutsuttiin aiemmin argumentilla "i32".

   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
 --> src/main.rs:6:22
  |
6 |     let b1 = closure(a1);
  |              ------- ^^ expected `i32`, found `f32`
  |              |
  |              arguments to this function are incorrect
  |
note: expected because the closure was earlier called with an argument of type `i32`
 --> src/main.rs:5:21
  |
5 |     let b = closure(a);
  |             ------- ^ expected because this argument is of type `i32`
  |             |
  |             in this closure call
note: closure parameter defined here
 --> src/main.rs:4:20
  |
4 |     let closure = |x| x;
  |                    ^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` (bin "playground") due to 1 previous error
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

1.3 Siirrä siepatut arvot pois sulkemisista ja Fn-ominaisuuksista

Kun sulkeminen kaappaa viittauksen arvoon tai omistuksen arvoon ympäristössä, jossa se on määritelty (vaikuttaen siten siihen, mitä, jos sellaista on, siirretään sulkemiseen), sulkemisen rungossa oleva koodi määrittää viittauksen myöhemmin, kun sulkeminen on arvioitu tai kuinka arvoa manipuloidaan (mikä vaikuttaa siihen, mitä sulkemisesta poistetaan). Suljinrunko voi tehdä minkä tahansa seuraavista: siirtää siepatun arvon pois sulkimesta, muokata siepattua arvoa, olla siirtämättä tai muokkaamatta arvoa tai ei koskaan kaapata arvoa ympäristöstä.

Tapa, jolla suljin kerää ja käsittelee ympäristön arvoja, vaikuttaa sulkimen toteuttamiin ominaisuuksiin. Ominaisuudet ovat tapa funktioille ja rakenteille määrittää, millaisia ​​sulkutyyppejä ne voivat käyttää.Riippuen siitä, kuinka suljinrunko käsittelee arvoja, sulkeminen toteuttaa automaattisesti ja asteittain yhden, kaksi tai kolmeFn piirre.

  1. FnOnce Koskee sulkemisia, jotka voidaan kutsua kerran. Kaikki sulkemiset toteuttavat ainakin tämän ominaisuuden, koska kaikki sulkemiset voidaan kutsua.Suljin, joka siirtää siepatut arvot pois sulkimen rungosta, vain toteuttaaFnOnce ominaisuus, koska sitä voidaan kutsua vain kerran.
  2. FnMut Sopii sulkimiin, jotka eivät siirrä kaapattua arvoa pois sulkimen rungosta, mutta se voi muuttaa siepattua arvoa. Tämän tyyppistä sulkemista voidaan kutsua useita kertoja.
  3. Fn Koskee sulkuja, jotka eivät siirrä kaapattua arvoa pois sulkimen rungosta eivätkä muuta siepattua arvoa, ja tietysti sisältävät sulkuja, jotka eivät kaappaa arvoa ympäristöstä. Tällaisia ​​sulkemisia voidaan kutsua useita kertoja muuttamatta niiden ympäristöä, mikä on tärkeää skenaarioissa, joissa sulkemisia kutsutaan useita kertoja samanaikaisesti.

Tässä on esimerkki siitä, kuinka sitä käytetään funktiossa FnOnce yleisenä rajoituksena ja varmista, että sulkemista kutsutaan vain kerran:

fn call_once<F, T>(f: F) -> T
where
    F: FnOnce() -> T, // 约束 F 为 FnOnce trait,意味着它接受一个空参数并返回 T 类型
{
    f() // 调用闭包并返回结果
}

fn main() {
    // 创建一个闭包,它捕获了 `value` 的所有权
    let value = 42;
    let consume = move || {
        let result = value; // 移动 `value`
        println!("The value is: {}", result);
        result // 返回结果
    };

    // 调用 `call_once` 函数,传入闭包
    let result = call_once(consume);
    println!("Result of the closure: {}", result);

    // 尝试再次使用 `consume` 将会导致编译错误,因为它已经消耗了 `value`
    // call_once(consume);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

operaation tulos

The value is: 42
Result of the closure: 42
  • 1
  • 2

Tässä esimerkissä määritellään yleinen funktio call_once, joka hyväksyy tietyn tyypin F Parametritf,sisään F On saavutettavaFnOnce piirre.Tämä tarkoittaaf on sulkeminen, joka hyväksyy tyhjät parametrit ja palauttaa tyypinT tulos.

olla olemassa main Toiminnossa luomme sulkemisenconsume, joka vangitsee value omistuksesta.Sitten soitetaancall_once toiminto, siirretty sisäänconsume Päättäminen.call_once Funktio kutsuu sulkemisen ja palauttaa tuloksensa.koskaconsume Sulkeminen on kulunutvalue, yritä soittaa uudelleen call_once Saman sulkemisen ohittaminen johtaa käännösvirheeseen.

Tässä on käyttöä FnMut Esimerkkejä ominaisuuksista.

fn apply_mut<F, T>(func: &mut F, num: i32) -> T
where
    F: FnMut(i32) -> T, // F 是一个可变闭包,接受一个 i32 类型的参数并返回类型为 T 的结果
{
    func(num) // 调用闭包并返回结果
}

fn main() {
    let mut count = 0;

    // 创建一个闭包,它接受一个 i32 类型的参数并将其加到 count 上
    let mut increment = |num: i32| -> i32 {
        count += num;
        count
    };

    // 使用 apply_mut 函数和 increment 闭包的引用
    let result: i32 = apply_mut(&mut increment, 5);
    println!("Result after applying increment: {}", result);

    // 再次使用 apply_mut 函数和 increment 闭包的引用
    let result: i32 = apply_mut(&mut increment, 10);
    println!("Result after applying increment again: {}", result);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

operaation tulos

Result after applying increment: 5
Result after applying increment again: 15
  • 1
  • 2

Tässä esimerkissäapply_mut Funktio hyväksyy aF muuttuva tyyppiviittaus&mut F parametrina (eliincrement Suljinta ei liikuteta ja sitä voidaan käyttää useita kertoja) ja ai32 tyyppiparametrejanumwhere lauseke määritteleeF Täytyy olla toteutusFnMut sulkeminen, joka hyväksyy ai32 type-parametrit ja palauttaa tyypinT tulos.main Funktioon luodaan muuttuva sulkuincrement, joka muokkaa siepattua muuttujaa count, käytä sitten apply_mut toiminto kutsua tätä sulkemista.jokainen puheluapply_mut Käyttääincrement sulkeminen jaincrement Sulkemisia muutetaancount arvo.

Ruosteessa,Fn Ominaisuus tarkoittaa, että sulkeminen ei ota omistukseensa sieppaamiaan muuttujia, eikä se muunna näitä muuttujia.Seuraava esimerkki näyttää, kuinka hyväksyntä määritelläänFn Sulje funktion argumenttina ja käytä sitä samanaikaisissa skenaarioissa.

use std::thread;

// 定义一个函数,它接受一个实现了 Fn(i32) -> i32 的闭包,并调用它
fn call_once<F, T>(func: F) -> T
where
    F: Fn(i32) -> T, // 指定 F 是一个接受 i32 并返回 T 的 Fn 闭包
{
    let result = func(42); // 调用闭包,传入一个 i32 类型的值
    result // 返回闭包的执行结果
}

fn main() {
    // 定义一个简单的 Fn 闭包,它接受一个 i32 类型的参数并返回两倍的该值
    let double = |x: i32| -> i32 {
        x * 2
    };

    // 创建多个线程,每个线程都使用相同的闭包
    let handles: Vec<_> = (0..5).map(|i| {
        let func = double; // 闭包可以被复制,因为它是 Fn 类型的
        thread::spawn(move || {
            let result = call_once(func); // 调用 call_once 函数,并传入闭包
            println!("Thread {} result: {}", i, result); // 打印结果
        })
    }).collect();

    // 等待所有线程完成
    for handle in handles {
        handle.join().unwrap();
    }
}
  • 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

Jokainen ajo ei ole tässä järjestyksessä, mikä säie suoritetaan ensin, riippuu järjestelmän nykyisestä aikataulusta.

operaation tulos

Thread 1 result: 84
Thread 2 result: 84
Thread 0 result: 84
Thread 4 result: 84
Thread 3 result: 84
  • 1
  • 2
  • 3
  • 4
  • 5

Tässä esimerkissä:

  1. Määritimme a call_once funktio, joka hyväksyy yleisen parametrinF,ja F täytyy olla tyytyväinenFn(i32) -> T piirteiden rajat.Tämä tarkoittaaF on sulku, joka hyväksyy ai32 type-parametrit ja palauttaa tyypinT tulos.
  2. olla olemassa main Funktiossa määrittelemme yksinkertaisen sulkemisendouble, joka hyväksyy a i32 tyyppiparametrejax ja palatax * 2 tulos.
  3. Käytämme map 5 säiettä luodaan ja jokainen säiettä kopioidaandouble Sulje ja kutsu se uuteen ketjuuncall_once funktio, välitetään sulkeminen argumenttinacall_once
  4. Jokaisessa säikeessä,call_once Funktio kutsutaan, sulkeminen suoritetaan ja tulos tulostetaan.
  5. Lopuksi käytämme join Menetelmä odottaa kaikkien säikeiden valmistumista.

2. Iteraattori

Iteraattorit antavat sinun suorittaa tietyn käsittelyn sekvenssin kohteille. Seuraavaksi esittelemme pääasiassa iteraattorien käytön elementtisekvenssien käsittelyssä ja silmukoiden VS-iteraattorien suorituskyvyn vertailun. Rustissa iteraattorit ovat laiskoja, mikä tarkoittaa, että mitään toimintoa ei suoriteta ennen kuin iteraattoria kuluttava menetelmä on kutsuttu.

2.1 Iteraattorin ominaisuus ja seuraava menetelmä

Kaikki iteraattorit toteuttavat funktion nimeltä Iterator on määritelty standardikirjaston piirteessä. Tämän ominaisuuden määritelmä näyttää tältä:

pub trait Iterator {
    type Item;

    fn next(&mut self) -> Option<Self::Item>;
    // 此处省略了方法的默认实现
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

type Item jaSelf::Item , ne määrittelevät ominaisuuteen liittyvän tyypin.Mutta toistaiseksi sinun tarvitsee vain tietää, että tämä koodi näyttää kuinka se toteutetaanIterator Ominaisuudet edellyttävät, että aItem tyyppi, tämäItem tyyppiä käytetään muodossanext Metodin palautusarvon tyyppi. toisin sanoen,Item Tyyppi on iteraattorin palauttaman elementin tyyppi.

next JooIterator Ainoat menetelmän toteuttajat vaaditaan määrittelemään.next Palauttaa iteraattorissa yhden kohteen kerrallaan kapseloitunaSome , kun iteraattori päättyy, se palaaNone

2.2 Iteraattorien käytön menetelmät

Iterator ominaisuuksilla on joukko erilaisia ​​menetelmiä, jotka on toteutettu oletuksena vakiokirjastossaIterator Kaikki nämä menetelmät löytyvät ominaisuuden vakiokirjaston API-dokumentaatiosta.Jotkut menetelmät vaativat määritelmänsänext menetelmällä, minkä vuoksi toteutuksessaIterator ominaisuus on otettava käyttöönnext menetelmän syistä.

Nämä puhelut next Menetelmien menetelmiä kutsutaan kuluviksi sovittimiksi, koska niiden kutsuminen kuluttaa iteraattoreita.

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];
    // 使用into_iter()将Vec转换为消费迭代器
    let numbers_iter = numbers.iter();
    let mut sum: i32 = numbers_iter
        // 使用sum适配器计算迭代器中所有元素的总和
        .sum();

    // 打印总和
    println!("The sum is: {}", sum);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Jos käytät sitä uudelleen numbers_iter Virhe ilmoitetaan.sum Metodi ottaa iteraattorin omistukseen ja kutsuu sitä toistuvastinext iteroida iteraattorin yli ja kuluttaa siten iteraattorin. Kun se iteroi jokaisen kohteen, se lisää jokaisen kohteen summaan ja palauttaa summan, kun iteraatio on valmis.siirtääsum Ei saa enää käyttäänumbers_iter, koska soittaa sum kun se ottaa iteraattorin omistukseensa.

2.3 Menetelmät muiden iteraattorien luomiseksi

Iterator Toinen ominaisuuksiin määritelty menetelmien luokka, nimeltään iteraattorisovittimet, mahdollistaa sen, että nykyinen iteraattori voidaan muuttaa erityyppiseksi iteraattoriksi. Useita iteraattorisovittimia voidaan kutsua ketjussa. Koska kaikki iteraattorit ovat laiskoja, täytyy kutsua kuluttava sovitinmenetelmä iteraattorisovittimen kutsun tulosten saamiseksi.

fn main() {
    let v1: Vec<i32> = vec![1, 2, 3];
    let v2: Vec<_> = v1.iter().map(|x| x * x).collect();
    assert_eq!(v2, vec![1, 4, 9]);
}
  • 1
  • 2
  • 3
  • 4
  • 5

Keräysmenetelmä kuluttaa iteraattorin ja kerää tulokset tietorakenteeseen.koskamap Saa sulkemisen, jonka avulla voit määrittää toiminnot, jotka haluat suorittaa jokaiselle elementille, jonka se kulkee.

2.4 Sulkimien käyttö, jotka vangitsevat ympäristönsä

Monet iteraattorisovittimet hyväksyvät sulkuja parametreiksi, ja yleensä iteraattorisovittimen parametriksi määritetty suljin on suljin, joka kaappaa sen ympäristön.

fn main() {
    let numbers = vec![1, 2, 3, 4, 5];

    // 使用into_iter()将Vec转换为消费迭代器
    let filtered_and_squared: Vec<i32> = numbers.into_iter()
        // 使用filter适配器过滤元素,接受一个闭包作为参数
        // 闭包捕获其环境,这里指numbers的元素
        .filter(|&x| x % 2 == 0) // 保留偶数
        // 使用map适配器对过滤后的元素进行变换,也接受一个闭包
        .map(|x| x * x) // 对每个元素进行平方
        // 使用collect适配器将结果收集到一个新的Vec中
        .collect();

    // 打印过滤和平方后的结果
    println!("The filtered and squared numbers are: {:?}", filtered_and_squared);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

operaation tulos

The filtered and squared numbers are: [4, 16]
  • 1

Tässä esimerkissäfilter jamap Molemmat ovat iteraattorisovittimia, ne hyväksyvät sulkemiset parametreina. Nämä sulkemiset vangitsevat ympäristönsä, eli iteraattorin elementit.olla olemassafilter Adapterissa, suljettava|&x| x % 2 == 0 Käytetään tarkistamaan, onko elementti parillinen luku, jos on, elementti säilyy.olla olemassamap Adapterissa, suljettava|x| x * x Käytetään jokaisen elementin neliöimiseen. Koska nämä sovittimet ovat kuluttajille tarkoitettuja, ne kuluttavat alkuperäisen iteraattorin, joten niitä ei voida käyttää uudelleen. viimeinkin,collect Sovitin kerää käsitellyt elementit uuteenVec keskellä.

2.5 Loop VS -iteraattorin suorituskyvyn vertailu

Iteraattorit, korkean tason abstraktiona, käännetään koodiksi, jonka suorituskyky on suunnilleen sama kuin käsin kirjoitetun matalan tason koodin. Iteraattorit ovat yksi Rustin nollakustannusabstraktioista, mikä tarkoittaa, että abstraktio ei aiheuta ajonaikaisia ​​ylimääräisiä kustannuksia.

fn main() {
    let numbers1 = (0..1000000).collect::<Vec<i64>>();
    let numbers2 = (0..1000000).collect::<Vec<i64>>();

    let mut sum1 = 0i64;
    let mut sum2 = 0i64;

    // 测量for循环的性能
    let start = std::time::Instant::now();
    for val in numbers1 {
        sum1 += val;
    }
    let loop_duration = start.elapsed();

    // 测量迭代器的性能
    let start = std::time::Instant::now();
    for val in numbers2.iter() {
        sum2 += val;
    }
    let iterator_duration = start.elapsed();

    println!("Iterator took: {:?}", iterator_duration);
    println!("For loop took: {:?}", loop_duration);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

operaation tulos

Iterator took: 12.796012ms
For loop took: 11.559512ms

Iterator took: 12.817732ms
For loop took: 11.687655ms

Iterator took: 12.75484ms
For loop took: 11.89468ms

Iterator took: 12.812022ms
For loop took: 11.785106ms

Iterator took: 12.78293ms
For loop took: 11.528941ms
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

Tästä esimerkistä näet, että iteraattorit ovat edelleen hieman hitaampia.

Viitelinkki

  1. Rustin virallinen verkkosivusto:https://www.rust-lang.org/zh-CN
  2. Ruosteen viralliset asiakirjat:https://doc.rust-lang.org/
  3. Rust Play:https://play.rust-lang.org/
  4. "Rust-ohjelmointikieli"