AL+

Limba: RO (română) EN (English)

Scrieți-ne: paul@alplustech.com

Privire înapoi: săptămâna 16–22 octombrie 2024

Rust impl Trait: împrumuturile fac parte din interfață

În Rust 1.82, capturarea precisă separă împrumuturile tipului ascuns de condițiile de durată de viață. Verificați contractul API și limitele ediției folosite.

Read this article in English

O funcție care întoarce un iterator cu tip ascuns trebuie să arate apelantului de ce împrumuturi depinde rezultatul. Rust 1.82 a făcut această relație mai ușor de exprimat prin capturarea precisă a parametrilor generici. La revizuire, verificați dacă semnătura descrie corect dependențele valorii întoarse. O condiție mai strictă privind duratele de viață, adăugată doar ca să dispară o eroare, poate restrânge inutil o interfață altfel folositoare.

Articolul privește retrospectiv, din noiembrie 2026, o schimbare din octombrie 2024. Versiunea stabilă din 17 octombrie a introdus use<..> pentru anumite tipuri impl Trait aflate în poziția de retur. Ediția Rust 2024 nu a devenit stabilă atunci. Distincția contează dacă o bibliotecă promite compatibilitatea cu un anumit compilator și cu o anumită ediție.

Ce trebuie să știe apelantul

Un tip de retur opac ascunde implementarea concretă și expune anumite trăsături, numite traits în Rust. Este util când apelantul are nevoie de iterare, fără să cunoască numele unui tip complicat de iterator. Ascunderea tipului nu poate ascunde obligațiile privind împrumuturile: apelantul trebuie să știe ce date de intrare limitează folosirea rezultatului.

În explicația din 5 septembrie 2024, echipa limbajului descrie capturarea drept permisiunea tipului ascuns de a folosi un parametru generic. O condiție de capturare exprimă această permisiune. O condiție outlives exprimă altceva: cât timp trebuie să rămână valid tipul întors. Confundarea celor două poate lega durate de viață ale datelor de intrare care nu trebuiau legate.

Pentru cei care întrețin biblioteca, consecința se vede în codul apelant. Acesta poate avea un context disponibil pentru scurt timp și date împrumutate independent. Dacă semnătura leagă accidental duratele lor de viață, apelantul poate fi nevoit să reorganizeze o operație validă. Interfața are astfel un cost, chiar dacă implementarea este scurtă și mesajul compilatorului a dispărut.

Exemplul care justifică sintaxa

Notele versiunii din octombrie prezintă un iterator cu durata de viață 'cx pentru context și 'a pentru date. Iteratorul păstrează contextul pentru jurnalizare și produce o referință la date. Folosirea condiției + 'cx pentru a face vizibilă durata contextului impune și o cerință outlives. Adăugarea condiției 'a: 'cx ar lega două durate care nu trebuie neapărat să aibă acea relație. Soluția din note este use<'cx, 'a>.

Exemplul susține o distincție precisă. Capturarea ambelor durate permite tipului ascuns să depindă de amândouă. Nu cere ca durata datelor să o depășească pe cea a contextului. Înainte să alegeți condiția, explicați ce referințe păstrează rezultatul și de ce. Schimbarea notației fără această explicație poate lăsa neatinsă eroarea de raționament.

Există și un exemplu mai simplu împotriva înlocuirii automate a tuturor condițiilor vechi. Anunțul precizează că, în exemplul scurt care întoarce chiar referința primită, + '_ și + use<'_> au comportament echivalent. Codul existent cu o condiție outlives nu este automat greșit. Trebuie stabilit dacă acea condiție exprimă o cerință reală sau una introdusă accidental.

Trei momente diferite

Articolul din septembrie explica direcția. În octombrie a apărut sintaxa stabilă, cu restricții. Rust 1.85, lansat la 20 februarie 2025, a stabilizat ediția 2024. Adoptarea sintaxei într-un proiect cu ediția 2021, în octombrie, era o schimbare diferită de migrarea ulterioară la noua ediție.

La stabilizarea din 1.82, lista explicită trebuia să includă toți parametrii generici de tip și parametrii generici const din domeniul vizibil. Sintaxa nu putea apărea în traits sau în implementările lor. În aceste contexte, duratele de viață vizibile erau deja capturate implicit. Un exemplu din documentația de astăzi nu dovedește că toate utilizările sale erau acceptate de compilatorul 1.82.

Ghidul curent al ediției, consultat pentru această retrospectivă, explică regula ediției noi: fără o condiție explicită, tipurile opace de retur capturează toți parametrii generici vizibili, inclusiv duratele de viață. În edițiile 2021 și anterioare, funcțiile obișnuite și metodele din implementări inerente nu capturau implicit toate duratele. Schimbarea ediției poate modifica astfel dependențele exprimate de interfață chiar dacă funcția are același corp.

Și capturarea excesivă restrânge apelantul

Ghidul ediției arată o funcție care capturează o durată de viață deși tipul ascuns nu o folosește. Un apelant care încearcă să obțină un rezultat opac 'static poate fi respins. Capturarea este o permisiune cu efect asupra contractului public, nu inventarul exact al referințelor păstrate de implementarea curentă.

De aceea, capturarea tuturor parametrilor nu este o regulă universală de proiectare. Când rezultatul deține datele proprii și nu depinde de un anumit împrumut, excluderea acelei durate poate păstra libertatea apelantului. Invers, excluderea unei durate de care implementarea chiar are nevoie nu ocolește regulile împrumuturilor. Semnătura și implementarea trebuie să corespundă.

Nicio asemenea adnotare nu poate valida o referință la date deja distruse. Funcționalitatea nu elimină conflictele de împrumut din alte cauze, nu repară transferul incorect al proprietății și nu dovedește că un API este ușor de folosit. Rezolvă o parte precisă a contractului tipurilor opace.

Alegeți interfața care se poate explica

Cea mai puternică alternativă este un tip de retur concret. Dacă numele lui este scurt, stabil și arată limpede relația, expunerea sa poate fi mai simplă pentru cei care întrețin codul. În schimb, apelanții pot ajunge să depindă de acel tip, iar schimbările implementării pot afecta compatibilitatea. Abstracția merită aleasă acolo unde flexibilitatea viitoare este utilă.

La revizuirea unei biblioteci, notați împreună compilatorul minim, ediția și dependențele intenționate. Cereți exemple de apeluri care trebuie acceptate și de apeluri care trebuie respinse. Sunt verificări propuse, nu teste efectuate pentru acest articol. Tratați modificările condițiilor de capturare ca schimbări de interfață.

În dezvoltarea software la comandă, motivul practic este întreținerea codului și a apelanților săi în timp. O semnătură fidelă împrumuturilor reale îi lasă următorului programator o problemă mai mică. Rust 1.82 oferă un vocabular mai exact; autorul API-ului rămâne responsabil pentru contractul ales.