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.
Sulkeminen, funktiomainen rakenne, joka voidaan tallentaa muuttujaan.
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"
}
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"
}
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);
}
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);
}
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
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);
}
Tässä esimerkissä:
square
, joka kaappaa viittauksella main
toiminnassanumber
muuttuja ja laske sen neliö.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.call_closure
toimintaa ja tahtoasquare
Sulkemiset välitetään argumentteina.koskasquare
Sulkuilla ei ole parametreja, ne voidaan välittää suoraan parametreinacall_closure
。call_closure
Funktio kutsuu sulkemisen ja tulostaa tuloksen. 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);
}
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 ;
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);
}
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);
}
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
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.
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.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.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);
}
operaation tulos
The value is: 42
Result of the closure: 42
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);
}
operaation tulos
Result after applying increment: 5
Result after applying increment again: 15
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
tyyppiparametrejanum
。where
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();
}
}
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
Tässä esimerkissä:
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.main
Funktiossa määrittelemme yksinkertaisen sulkemisendouble
, joka hyväksyy a i32
tyyppiparametrejax
ja palatax * 2
tulos.map
5 säiettä luodaan ja jokainen säiettä kopioidaandouble
Sulje ja kutsu se uuteen ketjuuncall_once
funktio, välitetään sulkeminen argumenttinacall_once
。call_once
Funktio kutsutaan, sulkeminen suoritetaan ja tulos tulostetaan.join
Menetelmä odottaa kaikkien säikeiden valmistumista.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.
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>;
// 此处省略了方法的默认实现
}
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
。
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);
}
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.
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]);
}
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.
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);
}
operaation tulos
The filtered and squared numbers are: [4, 16]
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ä.
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);
}
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
Tästä esimerkistä näet, että iteraattorit ovat edelleen hieman hitaampia.