Funcțiile de răspuns asincrone ar trebui să arate ce împrumută
Rust 1.85 permite funcțiilor de tip async closure să împrumute starea capturată peste await. Alegeți interfața după durata apelului și modul de execuție.
O funcție transmisă ca argument, care actualizează o colecție după ce așteaptă încheierea unei operații, are nevoie de o interfață care păstrează valid împrumutul necesar. Dacă biblioteca îi cere să producă un obiect future independent, apelantul poate fi nevoit să reorganizeze proprietatea datelor. Rust 1.85 a făcut utile funcțiile de tip async closure tocmai la această limită. Alegeți-le când apelul asincron trebuie să împrumute starea capturată. Păstrați datele deținute de operație când aceasta trebuie să părăsească acel context.
Această retrospectivă, scrisă în noiembrie 2026, privește lansarea din 20 februarie 2025. Interfața stabilă a inclus async || {} și trăsăturile AsyncFn, AsyncFnMut și AsyncFnOnce. Consecința pentru proiectare este un contract mai clar al funcției transmise ca argument. Lansarea nu dovedește că aplicația va fi mai rapidă și nici nu justifică înlocuirea tuturor funcțiilor care returnează deja un obiect future.
Obiectul future poate depinde de apel
În cazul unei funcții obișnuite care returnează un bloc asincron, trebuie urmărite două operații: apelarea funcției și execuția obiectului future produs. Diferența contează când a doua operație are nevoie de starea păstrată de prima. O funcție reutilizabilă nu poate lăsa pur și simplu o referință mutabilă la propria stare capturată să iasă printr-un obiect future arbitrar, descris independent.
RFC 3668, cu data de început afișată 25 iunie 2024, explică motivul pentru care este necesar acest împrumut și problema asociată a duratelor de viață cuantificate universal. Un singur parametru Fut, ales separat, nu poate descrie orice obiect future al cărui tip depinde de durata de viață a argumentului fiecărui apel. Constrângerile pentru funcții asincrone pot exprima această relație. RFC-ul explică proiectarea; notația propusă și discuția despre mecanismele instabile nu stabilesc sintaxa stabilă din februarie 2025.
Luați ca exemplu de proiectare un import secvențial. Funcția transmisă așteaptă rezultatul unei căutări și îl adaugă într-o colecție capturată din contextul exterior. Importatorul așteaptă fiecare apel înainte să înceapă următorul. O funcție care împrumută starea se potrivește acestui mod de lucru: împrumutul aparține apelului asincron curent, iar contextul exterior răspunde în continuare de colecție. Exemplul este o propunere, nu descrierea unei implementări AL+.
Interfața ar trebui să facă vizibilă presupunerea execuției secvențiale. Apelantul trebuie să înțeleagă când poate folosi din nou starea capturată. Dacă un obiect future păstrează un împrumut mutabil, o altă utilizare incompatibilă nu poate începe doar fiindcă funcția a returnat acel obiect. Contează momentul în care se încheie împrumutul, care poate continua peste un await.
Proprietatea capturilor și împrumutul sunt alegeri separate
Mutarea valorilor într-o funcție schimbă cine deține acele valori. Nu stabilește, singură, dacă obiectele future produse prin apeluri repetate împrumută din starea funcției. Distincția se pierde ușor dacă move este tratat ca remediu general pentru erorile legate de duratele de viață. Analizați separat cât trebuie să existe funcția și cât trebuie să dureze fiecare apel.
Pentru autorul unei biblioteci, analiza începe cu ordinea execuției. Biblioteca așteaptă apelul înainte să continue? Poate păstra simultan mai multe apeluri în desfășurare? Reține funcția pentru mai târziu? Răspunsurile stabilesc ce trebuie să furnizeze apelantul. Un exemplu comod, cu un singur apel, poate ascunde restricții care devin importante când biblioteca repetă sau suprapune apelurile.
Consecința pentru firmă apare la mentenanța integrării. Dacă biblioteca lucrează secvențial, obligarea apelanților să introducă proprietate partajată și sincronizare poate ascunde acest comportament. În sens invers, acceptarea unei funcții care împrumută stare, deși execuția trebuie să continue independent în fundal, pune interfața în conflict cu modul de execuție. Contractele clare reduc raționamentul despre proprietate pe care fiecare integrare trebuie să îl refacă. Acesta este un argument de proiectare, nu o economie măsurată la livrare.
Pornirea unei sarcini independente schimbă recomandarea
Cel mai puternic motiv pentru a păstra o funcție cu date proprii, care returnează un obiect future, este nevoia reală de execuție independentă. O sarcină care poate continua după încheierea apelantului nu se poate baza pe împrumutul unei colecții locale care urmează să dispară. Contează și cerințele executorului, inclusiv dacă obiectul future trebuie să poată trece între fire de execuție.
La această limită, un mesaj cu date proprii sau o intrare deținută explicit de operație poate fi mai ușor de înțeles decât o interfață bazată pe împrumut. O referință partajată poate fi potrivită când partajarea este necesară, cu costul sincronizării și comportamentul la eroare analizate explicit. Funcțiile de tip async closure nu elimină aceste decizii. Acceptarea unei funcții asincrone nu dovedește automat că fiecare obiect future returnat satisface constrângerile interfeței care pornește sarcina.
Nici funcționalitatea stabilă istorică nu ar trebui extinsă prin copierea metodelor interne sau a tipurilor asociate din exemplele RFC într-o interfață publică. Folosiți interfața stabilă a versiunii alese și examinați cerințele greu de exprimat prin contractul funcției. Dacă funcția existentă, cu date proprii, este clară și suficientă, păstrarea ei este o decizie rezonabilă.
Urmăriți operația care păstrează împrumutul
Subiectul diferă de regulile de captură precisă pentru impl Trait în poziția de retur. Acelea stabilesc ce parametri generici poate captura tipul opac returnat. Aici contează dacă obiectul future produs de un apel poate împrumuta din mediul capturat al funcției. Ambele probleme implică durate de viață, dar schimbarea unei semnături cu tip opac nu stabilește cum trebuie apelată o funcție asincronă.
La analiza unui proiect de dezvoltare software la comandă, alegeți o funcție transmisă ca argument care provoacă acum dificultăți de proprietate. Descrieți cine o deține, ce stare împrumută și când poate apelantul folosi din nou acea stare. Precizați apoi dacă operația este așteptată local sau pornită independent. Preferați împrumutul în primul caz, când este necesar, și predarea datelor proprii în al doilea, când este necesară independența. Astfel, sintaxa are un scop concret, iar următorul responsabil de mentenanță poate examina modul de execuție.
Paul