Internet Windows Android

Rust: începerea cu un nou limbaj de programare de la specialiștii Mozilla. Limbajul Rust și de ce ar trebui să îl mâncați Rust de unde să începeți programarea


Ne-a plăcut foarte mult articolul „Criticizing the Rust Language and Why C/C++ Will Never Die”. I-am sugerat autorului să traducem articolul în engleză și să îl publicăm pe blogul nostru. El a fost de acord și suntem încântați să prezentăm acest articol în rusă și engleză. Articolul original este localizat.

Articolul original este postat (text în rusă). Articolul a fost publicat pe blogul nostru cu acordul autorului.

Notă: presupun mai jos că Rust este o încercare de a crea un limbaj rapid și sigur. La urma urmei, băieții de la Mozilla au făcut-o ca instrument de dezvoltare a motorului de browser. Dacă acesta este doar un alt limbaj sigur, atunci devenim ciudați. Există deja un ban într-o duzină de limbi sigure diferite, fiecare va găsi după bunul său plac. Și dacă scopul nu este înlocuirea C ++, atunci (1) de ce este submulțimea nesigură făcută în limbaj? (2) de ce a fost convenabil să eliminați fluxurile ușoare din limbă? Cu alte cuvinte, în acest caz, ceea ce se întâmplă nu are deloc sens.

Dacă citiți brusc forumul linux.org.ru, observ că aceasta nu este lista cu 10 motive pur tehnice pentru a nu vă place Rust, despre care a fost discutată în acest thread. Ca discuția cu Skype drag tovarăș @ sum3rman Există mai mult de o părere despre cât de „tehnice” sunt aceste motive. În general, am făcut o listă de smochine, dar unele dintre punctele din ea, cele mai interesante, poate, încă mă îndrăznesc să le citez. De fapt, există destule motive simple, non-tehnice, pentru ochi.

Faptul că C/C++ nu va merge nicăieri în viitorul apropiat este clar pentru orice persoană treaz. Nimeni nu va rescrie aproape toate aplicațiile desktop, nucleele sistemului de operare, compilatoarele, motoarele de jocuri și browser, mașinile virtuale, bazele de date, arhivele, codecurile audio și video, tone de alte biblioteci Sish și așa mai departe. Acesta este o mulțime de cod rapid, depanat și testat în timp. Rescrierea este foarte, foarte costisitoare, riscantă și, să fiu sincer, are sens doar în mințile distorsionate ale celui mai încăpățânat Rust „Oman. Cererea de programatori C/C++ a fost și va fi grozavă pentru un perioadă lungă de timp.

Bine, ce zici să folosești Rust când scrii cod nou?

Să ne amintim că aceasta nu este prima încercare de a face C/C++ „mai corect”. Să luăm limbajul D. A apărut în 2001, un limbaj foarte bun. Nu există posturi vacante, instrumente de dezvoltare normale sau povești de succes deosebit de remarcabile. Proiectul OpenMW a fost scris inițial în D, iar apoi brusc au decis să-l rescrie în întregime în C++. După cum recunosc dezvoltatorii, au primit o mulțime de scrisori în stilul „proiect grozav, am fi bucuroși să contribuim la el, dar nu știm și nu vrem să cunoaștem acest D prost”. Wikipedia raportează că, pe lângă D, au existat o mulțime de alte încercări de a ucide C++ într-o măsură sau alta, de exemplu, Vala, Cyclone, Limbo, BitC. Câți oameni au auzit de asemenea limbi?

Cred că este timpul să învățăm din istorie. Nicio persoană sănătoasă nu va trage o nouă limbă într-un proiect până când măcar nu îi arăți instrumente de dezvoltare normale, nu îi spui câteva povești de succes și nu arăți o duzină de programatori în această limbă care locuiesc în apropiere. Programatorii, poate, cu excepția celor mai tineri, nu își vor pierde niciodată timpul și sănătatea învățând următoarea limbă cea mai corectă până când nu le arăți instrumente de dezvoltare normale (nu artizanat precum Racer), câteva zeci de mii de biblioteci gata făcute (nu „experimental”, „instabil” și așa mai departe), nu spune câteva povești de succes și arăta o duzină de posturi libere în orașul lor. Problema cu puiul și ouăle. Foarte rar, această problemă poate fi rezolvată cu succes (de exemplu, Scala poate fi citată aici ca exemplu), în principal datorită investiției de timp și bani din partea unei companii mari (Google, Typesafe), care, din anumite motive , este interesat de popularizarea limbii.

După cum am menționat, motivele non-tehnice sunt mai mult decât suficiente. Totuși, din pur curiozitate, să încercăm să ne imaginăm pentru o secundă că nu sunt acolo. Atunci nu există niciun motiv să nu scrieți în Rust? Se pare că aceasta este, de asemenea, cel puțin o întrebare foarte mare.

C/C++ este criticat pentru diferite lucruri. Apropo, este foarte des criticat de cei care nici măcar nu au văzut codul C++ în producție de la distanță. Pe scurt și clar, problema poate fi descrisă după cum urmează: C++ este foarte rapid (și, de asemenea, nu solicită memorie, puterea bateriei etc.), dar nu este sigur în sensul că vă permite să depășiți limitele matricelor. , accesați din greșeală bucățile de memorie eliberate și așa mai departe. La un moment dat, această problemă a dus la apariția unei mase de limbaje sigure, cum ar fi Java, C #, Python și altele. Dar s-a dovedit că aceste limbaje, în comparație cu C++, sunt prea solicitante de resurse și au alte dezavantaje, amintiți-vă cel puțin de oprirea inevitabilă a lumii în timpul colectării gunoiului. Prin urmare, oamenii se luptă cu sarcina de a face limbajul la fel de rapid ca C++, dar și sigur. Una dintre aceste limbi este Rust.

Rugina este într-adevăr sigură, dar, din păcate, departe de a fi rapidă. În ceea ce privește viteza la momentul scrierii acestui articol, Rust este comparabil cu Java, Go și Haskell:

Sper din tot sufletul ca in timp sa fie cumva overclockat, dar pana atunci, din punct de vedere al compromisului de viteza si securitate, nu este cu mult mai interesant decat Scala sau Go. Până acum, întrebarea rămâne dacă este posibil să facem un limbaj rapid și sigur, sau verificări constante pentru depășirea limitelor unei matrice, legarea sigură în jurul legăturilor la bibliotecile C și așa mai departe, faceți automat orice limbă de 2 ori mai lent decât C/C++.

Cum este Rust de fapt sigur? În termeni simpli, este un limbaj cu un analizor de cod static încorporat. Un analizor static cu adevărat foarte tare care prinde toate erorile tipice pentru C ++, în plus, nu doar cele legate de managementul memoriei, ci și de multithreading. Am trecut linkul către obiectul mutabil către un alt fir prin canal și apoi am încercat să folosesc acest link eu însumi - asta e tot, nu se va compila. E foarte tare.

Se susține adesea că doar 10% din cod este executat în 90% din timp (care, după cum am înțeles, este o simplă regulă de bază - nu s-a găsit rapid nicio cercetare riguroasă pe acest subiect). Prin urmare, cea mai mare parte a programului poate fi scrisă în Rust sigur, iar 10% din codul fierbinte poate fi scris într-un subset nesigur, iar încetineala implementării actuale a Rust nu este cu adevărat o problemă. Ok, dar apoi se dovedește că Rust nu este deloc necesar, pentru că pot scrie 90% din cod în Go și 10% în C. Numai căutătorii de gloanțe de argint și ereticii divorțați de realitate vor folosi Rust doar pe motiv că 100% din program poate fi scris într-o singură limbă, așa cum ar fi. Deși, în realitate, acestea sunt două dialecte ale aceleiași limbi, ceea ce nu este atât de diferit de o grămadă de Java plus C sau Go plus C.

De fapt, regula 10:90 este încă o minciună. Urmând această logică, puteți rescrie 90% din WebKit, 90% din VirtualBox sau 90% din GCC în Java și să obțineți același rezultat. Evident că nu este cazul. Chiar dacă ideea nu este că într-o serie de programe această atitudine este foarte diferită, atunci ai grijă la mâini. Să presupunem că întregul program este scris în C/C++ nesigur și timpul său de execuție, relativ vorbind, este 0,9 * 1 (o mică parte din codul cald) + 0,1 * 1 (mult cod rece) = 1. Acum să comparăm cu un program într-un limbaj sigur cu inserții în Si: 0,9 * 1 + 0,1 * 2 = 1,1, convențional 10% din diferență. Este mult sau puțin? Depinde de scara ta. În cazul Google, chiar și câteva procente pot economisi milioane de dolari (vezi paragraful 5 din lucrare, „Utilizare”). Sau imaginați-vă că, odată cu următoarea actualizare, JVM-ul începe brusc să necesite cu 10% mai multe resurse! Mi-e teamă să ghicesc chiar și câte zerouri vor fi în cifra obținută după transferul dobânzii în bani americani! 10% este dofig în sarcinile în care sunt folosite C și C++.

Repetăm ​​„optimizarea prematură este rădăcina tuturor relelor” ca mantra. Dar, la propriu, să folosim sortarea cu bule peste tot în loc de sortarea rapidă. Nu știm sigur că programul va încetini exact în acest loc! Ce rost are să împachetezi contoare obișnuite ale unor acțiuni în actori sau în memoria tranzacțională, dacă poți folosi imediat un atom mai eficient? Și, în general, în cazuri banale, nu are sens să forțați inițializarea tuturor variabilelor, să faceți o grămadă de verificări suplimentare și așa mai departe. Să obținem în final nu o accelerație de 10%, ci 2-5%. Nici acest lucru nu este rău deloc, dacă a durat doar câteva minute în plus de gândire. Și așa cum am aflat deja, în problemele rezolvate în C/C++, aceasta poate fi o mare diferență! Atunci cine a spus că găsirea unui punct fierbinte, rescrierea codului (posibil mult cod) și demonstrarea că este cu adevărat mai rapid este mai ușor decât să te gândești la performanță în avans?

Pe lângă compromisul dintre viteză și siguranță, am și întrebări despre designul limbajului în sine. În special, în ceea ce privește cele cinci tipuri de indicatoare. Pe de o parte, nu este rău când un programator se gândește unde sunt variabilele, pe stivă sau în heap, și mai multe fire de execuție pot sau nu să funcționeze cu ele simultan. Dar, pe de altă parte, imaginați-vă că scrieți un program și s-a dovedit că variabila nu ar trebui să trăiască în stivă, ci în heap. Rescrii totul pentru a folosi Box. Prin urmare, înțelegi că chiar ai nevoie de Rc sau Arc. Rescrie din nou. Și apoi îl suprascrii din nou cu o variabilă obișnuită pe stivă. Toate acestea - fără un IDE normal la îndemână. Și sezonul regulat nu va ajuta. Ei bine, sau doar în stilul „Vec >>> "bună ziua, Java! Cel mai trist lucru este că compilatorul știe deja despre durata de viață a tuturor variabilelor, ar putea afișa automat toate aceste casete, Arc și așa mai departe. Dar din anumite motive, această parte a lucrării este lăsată pentru programator. Mult mai convenabil ar fi ușor să scrieți val (în mileniul al treilea!) și, acolo unde este necesar, să indicați în mod explicit Box sau R. Dezvoltatorii Rust în acest sens au dat peste cap toată ideea.

Din această cauză, în special, domeniul de aplicare al Rust este foarte restrâns. Nimeni în minte nu ar scrie partea web și server într-o astfel de limbă. Mai ales având în vedere că nu oferă avantaje semnificative față de aceleași limbi sub JVM. Și Go with normal lightweight threads (nu futures) pare mult mai atractiv pentru aceste sarcini. Cu futures, pentru a nu te împușca în picior, mai trebuie să înveți să lucrezi și spui „limbaj sigur”. Da, aceste limbi au propriile lor caracteristici, iau aceeași oprire în lume, dar această problemă poate fi rezolvată, atât prin tăierea în microservicii, cât și prin alte metode. Și da, nimeni nu va traduce Rust în JavaScript, nu va scrie scripturi pentru aspect în AWS sau nu îl va folosi ca limbaj de interogare pentru MongoDB. Cu greu vor scrie nici pentru Android, dar din alt motiv - există mult mai multe arhitecturi, cu JVM este mult mai ușor. Dacă te-ai gândit brusc că Rust este „potrivit pentru toate sarcinile”, trebuie să te dezamăgesc.

Ei bine, la grămadă:

  • Macro-uri, ca o rezervă la verbozitatea excesivă cauzată de absența excepțiilor normale. Am scris deja despre problemele metaprogramarii, în special, este puțin probabil să vedem un IDE normal pentru Rust din cauza asta. Și nu sunt sigur, dar se pare că macrocomenzile din Rust nu au nici măcar spații de nume.
  • Oamenii sunt idioți, iar cargo încurajează cu tărie tragerea de pachete direct din depozitele git, ocolind Crates.io. Drept urmare, există o probabilitate mare de a obține aceeași mizerie cu pachetele ca în lumea Erlang cu Rabar-ul său.Apropo, în lumea Go, se pare, aceeași situație.
  • Ca multe limbi noi, Rust urmează calea simplificării. În general, înțeleg de ce nu există o moștenire normală și excepții în ea, dar însuși faptul că cineva decide astfel de lucruri pentru mine lasă un gust neplăcut. C++ nu limitează programatorul la ce să folosească și ce nu.
  • Dacă ar fi să mergem pe calea simplificării, atunci ar trebui să aruncăm toate aceste extensii ale limbajului. În rest, se pare, ca în lumea lui Haskell, fiecare programator scrie în dialectul său.
  • Indicatoarele inteligente, dacă există, sunt departe de a fi gratuite și nu duc la timpi previzibili de colectare a gunoiului. Unele fire sunt brusc privilegiate să elibereze o structură de date foarte profundă. În timp ce el se plimbă prin labirintul de verigi moarte, firele care depind de el se plictisesc cu răbdare. Aceeași problemă există și în Erlang cu grămezile lui mici, eu însumi am observat-o de mai multe ori. Indicatoarele inteligente au propriile lor probleme, aceeași fragmentare a memoriei și scurgeri. Am uitat wikpointer-ul din structura ciclică, atât. Și aceasta este într-o limbă care pretinde a fi sigură. Dacă doriți un timp GC previzibil, fie studiați comportamentul aplicației dumneavoastră sub încărcare și luați măsuri (amintiți-vă cel puțin aceleași grupuri de obiecte) dacă ora GC nu vă convine, fie gestionați manual memoria.
  • A văzut cineva o descriere strictă a semanticii Rust? Are măcar un model de memorie? De asemenea, pentru mine un limbaj „sigur” care „demonstrează corectitudinea” programelor, care de fapt poate interpreta codul sursă în zece moduri diferite, ha!
  • Nu pot să nu-ți amintesc asta problema sunt aproape întotdeauna oamenii, nu tehnologia... Dacă obțineți cod C++ sau Java prost care încetinește brusc, nu este pentru că tehnologia este proastă, ci pentru că nu ați învățat cum să o utilizați corect. De asemenea, vei fi nemulțumit de Rust, dar din motive diferite. Nu este mai ușor să înveți cum să folosești instrumentele mai populare și să începi să le iubești?

În general, în următorii 5 ani îmi va fi mai bine să-mi investesc timpul în învățarea C/C++ decât Rust. C++ - acesta este un standard industrial... Diverse probleme au fost rezolvate cu succes în această limbă de peste 30 de ani. Și Rust și altele asemenea sunt jucării de neînțeles cu un viitor vag. S-a vorbit despre moartea iminentă a lui C ++ cel puțin încă din anii 2000, dar scrierea în C / C ++ în acest timp nu a devenit mai puțin. Dimpotrivă. Și vedem că limbajul este în curs de dezvoltare (C ++ 11, C ++ 14), apar instrumente noi pentru el (nu uitați, cel puțin CLion și Clang) și există doar o grămadă de posturi vacante corespunzătoare.

Un programator C++ poate întotdeauna să-și găsească cu ușurință un loc de muncă cu un salariu mai mult decât decent și, dacă este necesar, să-l reconstituie rapid pe Rust. Reversul este foarte, foarte îndoielnic. Apropo, limba, dacă este ceva, este departe de a fi singurul și nu un factor decisiv în alegerea unui nou loc de muncă. În plus, un programator C/C++ cu experiență poate săpa cu ușurință în sursele kernel-ului PostgreSQL sau Linux, să folosească instrumente moderne de dezvoltare puternice și, de asemenea, să aibă la dispoziție multe cărți și articole (de exemplu, despre OpenGL).

Economisiți timp și sănătate, nu aveți atât de multe pe cât pare!

În 2013, Mozilla a colaborat cu Samsung pentru a dezvolta un nou motor de browser web Servo. A fost creat special pentru procesoarele multi-core ale dispozitivelor mobile, este capabil să împartă sarcinile în fire paralele și să reducă semnificativ timpul de încărcare a paginilor web. Servo este scris în întregime în limbajul de programare Rust, pe care Mozilla l-a dezvoltat pentru scrierea aplicațiilor mobile.

Despre limbaj

Rust este un limbaj de programare procedural care acceptă o varietate de stiluri de codare. Dezvoltatorul Graydon Hor a început să creeze limbajul în 2006, iar Mozilla sa alăturat proiectului trei ani mai târziu. În 2010, Rust a fost prezentat la Summit-ul Mozilla. În același an, dezvoltarea a fost transferată la un compilator scris în Rust. Compilatorul a folosit ca bază de date sistemul universal de analiză și transformare a programelor LLVM.

Prima versiune stabilă a limbii a fost lansată în 2015. După lansarea versiunii alfa, Rust a suferit modificări - în compilator au rămas doar caracteristici gata făcute care nu se vor schimba. Orice altceva a fost mutat în secțiunea experimentală.

În centrul limbii, Graydon Khor a pus concepte precum:

  • Securitate. Rust conține o serie de restricții de programare care sunt activate implicit. Pentru a le dezactiva în blocuri și funcții, este necesară eticheta „nesigur”.
  • Viteză. Limbajul este comparabil ca viteză cu un alt limbaj de programare C++, ceea ce oferă un număr clar de avantaje pentru programator.
  • Paralelism. Sistemul poate efectua mai multe calcule în același timp, împreună cu acestea pot interacționa între ele.
  • Concizie. Primele cuvinte cheie din Rust aveau cinci caractere. Dar mai târziu această restricție a fost eliminată.

Un exemplu de unul dintre primele coduri Rust

Cu toate acestea, Rust nu este lipsită de dezavantaje, cele mai izbitoare dintre ele sunt:

  • Redundanța codului.
  • Lipsa literaturii pentru învățarea limbilor străine.
  • Claritate în introducerea opțiunilor de compilare. Acest lucru nu se potrivește întotdeauna programatorilor experimentați, deoarece nu există reguli similare în alte limbi.

Cu toate acestea, limba este actualizată și completată în mod regulat: actualizările sale sunt lansate la fiecare 6 săptămâni.

Comparând Rust cu C++

Creatorii Rust îl consideră a fi succesorul C ++, care a apărut la începutul anilor 1980, când dezvoltatorul a venit cu mai multe îmbunătățiri pentru limbajul C. Prin urmare, merită să comparăm limbajul tânăr și încă în schimbare cu timpul testat.

  • Acces la memoria de la distanță. În C++, ștergerea unei variabile poate cauza o serie de probleme. Complicații de acest fel nu sunt posibile în Rust, deoarece nu există comenzi pentru ștergerea memoriei. Compilatorul succesorului va raporta că codul scris conține o eroare, iar compilatorul C++ va scoate rezultatul fără valorile șterse, fără a raporta măcar problema.
  • Punct şi virgulă. Adăugarea unui punct și virgulă suplimentar la codul dvs. va provoca o eroare în C ++, în timp ce în Rust corpul buclei este închis între acolade.
  • Cod nesigur. Rust are o etichetă „nesigur” care izolează codul principal de codul nesigur. În viitor, atunci când revizuiți codul, acest lucru vă permite să restrângeți căutarea vulnerabilităților.

Firefox a fost implementat în C++: acest limbaj capricios a cerut o atenție sporită la detalii. În caz contrar, erorile s-au transformat în vulnerabilități grave. Rust a fost conceput pentru a rezolva această problemă.

Perspective

Limbajul de programare Mozilla ocupă în mod constant locul 23 în clasamentul RedMonk din T3 2018. Experții consideră că nu riscă să-și îmbunătățească poziția. Indiferent, în august 2018, creatorii au lansat Rust 1.28 actualizat.

După ce Rust a fost lansat în 2015, 74% dintre dezvoltatori au vrut să-l vadă, potrivit Stack Overflow. Cu toate acestea, în 2016 a trecut pe primul loc: 79% dintre utilizatori l-au numit pe Rust limbajul de programare preferat și și-au exprimat dorința de a continua să lucreze cu el. Rust a ocupat primul loc la acest parametru și în 2018.

Stack Overflow este un sistem popular de întrebări și răspunsuri de codificare dezvoltat în 2008.

Popularitatea Rust este confirmată de numărul de companii care îl folosesc în dezvoltarea lor. Lista include în prezent 105 organizații.



Astăzi, sintaxa Rust este acceptată în vim și emacs folosind fișierele de sintaxă furnizate împreună cu compilatorul.
Există, de asemenea, pachete de sintaxă pentru editorul proprietar Sublime Text 2 și editorul gratuit Kate. Nu există încă suport pentru Rust în IDE. Suportul pentru depanare pare să lipsească, de asemenea.

Următoarele utilități sunt furnizate împreună cu compilatorul rustc:
> rustdoc- utilitate pentru generarea automată a documentației din cod sursă precum Doxygen;
> ruginăpkg- un manager de pachete care vă permite să instalați cu ușurință pachete și biblioteci suplimentare;
> rusti- așa-numitul utilitar REPL (read-eval-print-loop). În esență, este un interpret de testare care preia o expresie Rust din linia de comandă, o compilează în reprezentarea LLVM internă, execută și scoate rezultatul;
> rugini- un utilitar universal care lansează alte utilitare sau compilatorul, în funcție de parametri. Nu a funcționat niciodată pentru mine.

Toată documentația lingvistică disponibilă este colectată pe site-ul oficial www.rust-lang.org. Există un manual detaliat (http://static.rust-lang.org/doc/tutorial.html) - documentație formală cuprinzătoare despre toate nuanțele de sintaxă, model de memorie, sistem de rulare etc., precum și documentație privind bibliotecă de bază încorporată și biblioteca standard std. Toată documentația este în limba engleză. Nu există materiale relevante în limba rusă și câteva articole de recenzie disponibile au devenit deja foarte depășite.

Ideologie și sintaxă


Rust este un limbaj asemănător C care folosește acolade pentru a marca blocuri de cod. Limbajul este „multi-paradigma”, adică. vă permite să scrieți cod într-un mod imperativ procedural, orientat pe obiecte, concurent sau funcțional. Rust se compilează în binare native pe orice platformă acceptată (folosește LLVM ca backend). În teorie, codul Rust ar trebui să fie la fel de rapid ca codul C/C++. Rust este comercializat ca limbaj de sistem, dar nu are suport încorporat pentru blocuri de cod de asamblare ca în limbajele de sistem „adevărate” C, C++ sau D.

Modelul de memorie al lui Rust nu permite în mod nativ indicatori nuli sau suspendați și depășiri de buffer. Există un colector de gunoi opțional care funcționează doar într-un singur fir de cod. Limbajul are suport încorporat pentru multitasking ușor și mesagerie thread-to-thread. Memoria partajată nu există în Rust. Toate variabilele sunt subdivizate în variabile de stivă, variabile heap pentru un anumit fir și așa-numitele variabile heap „de schimb”, care pot fi citite de toate firele de execuție, dar nu pot fi modificate de acestea. Acest lucru elimină automat blocajele, care sunt considerate flagelul programării cu mai multe fire. ABI-ul limbajului este compatibil cu C, astfel încât programele Rust se pot conecta cu bibliotecile scrise în C fără wrapper-uri suplimentare. Pentru nevoile de programare a sistemului de nivel scăzut și pentru a asigura compatibilitatea cu C, limbajul are un mod special „nesigur” fără a verifica corectitudinea pointerilor. În ceea ce privește ideologia sa, Rust este cel mai apropiat de limba Go. La fel ca în Go, accentul principal este pus pe simplitatea programării multi-threaded și pe viteza de dezvoltare a aplicațiilor la scară largă, iar sintaxa este la fel de neobișnuită și oarecum surprinzătoare în unele locuri. În același timp, Rust nu este la fel de minimalist ca Go și pretinde a fi un limbaj de sistem.

Sintaxa lui Rust este în mare parte împrumutată din C și C ++, cu o notă de idei din Go, C #, Haskell, Python și Ruby. Nu voi descrie exhaustiv sintaxa limbajului, ci mă voi concentra doar pe cele mai interesante concepte.

Rugini este un nou limbaj de programare experimental dezvoltat de Mozilla. Limbajul este compilat și multi-paradigmă, este poziționat ca o alternativă la C/C++, ceea ce este interesant în sine, deoarece nu există atât de mulți concurenți la competiție. Gândiți-vă la D-ul lui Walter Bright sau la Go-ul lui Google.
Rust acceptă programarea funcțională, paralelă, procedurală și orientată pe obiecte, de ex. aproape întreaga gamă de paradigme utilizate efectiv în programarea aplicată.

Nu intenționez să traduc documentația (în plus, este foarte puțină și în continuă schimbare, deoarece nu a existat încă o lansare oficială a limbii), în schimb vreau să evidențiez cele mai interesante caracteristici ale limbii. Informațiile au fost culese atât din documentația oficială, cât și din foarte puținele mențiuni ale limbii de pe internet.

Prima impresie

Sintaxa limbajului este construită în stilul tradițional asemănător C (ceea ce este o veste bună, deoarece acesta este deja un standard de facto). Desigur, au fost luate în considerare erorile de proiectare C / C ++ binecunoscute.
Tradiționalul Hello World arată astfel:
utilizați std; fn main (args :) (std :: io :: println ("bună ziua de la" + args + "!");)

Un exemplu este puțin mai complicat - funcția pentru calcularea factorialului:

Fn fac (n: int) -> int (fie rezultat = 1, i = 1; în timp ce i<= n { result *= i; i += 1; } ret result; }

După cum puteți vedea din exemplu, funcțiile sunt declarate în stilul „funcțional” (acest stil are unele avantaje față de tradiționalul „int fac (int n)”). V-om vedea inferență de tip automat(cheie let), în timp ce argumentul nu are paranteze (similar cu Go). Compactitatea cuvintelor cheie este, de asemenea, imediat evidentă. Creatorii lui Rust au făcut cu adevărat toate cuvintele cheie cât mai scurte posibil și, să fiu sincer, îmi place asta.

Caracteristici sintactice mici, dar interesante

  • Puteți utiliza litere de subliniere în constantele numerice. Un lucru la îndemână, acum această caracteristică este adăugată în multe limbi noi.
    0xffff_ffff_ffff_ffff_ffff_ffff
  • Constante binare. Desigur, un programator adevărat ar trebui să transforme bin în hex în capul său, dar acest lucru este mai convenabil! 0b1111_1111_1001_0000
  • Corpurile oricăror operatori (chiar și cele care constau dintr-o singură expresie) trebuie să fie închise între acolade. De exemplu, în C ai putea scrie dacă (x> 0) foo (); , în Rust trebuie să puneți bretele în jurul foo ()
  • Dar argumentele operatorilor if, while și altele asemenea nu trebuie să fie incluse în paranteze.
  • în multe cazuri, blocurile de cod pot fi considerate expresii. În special, acest lucru este posibil, de exemplu:
    fie x = if the_stars_align () (4) else if something_else () (3) else (0);
  • sintaxă pentru declararea funcțiilor - mai întâi cuvântul cheie fn, apoi lista de argumente, după nume se indică tipul argumentului, apoi, dacă funcția returnează o valoare, săgeata „->” și ​​tipul valorii returnate
  • variabilele sunt declarate într-un mod similar: cuvântul cheie let, numele variabilei, după variabilă, puteți specifica tipul prin două puncte și apoi alocați o valoare inițială.
    lasă să numere: int = 5;
  • implicit toate variabilele sunt imuabile; cuvântul cheie mutabil este folosit pentru a declara variabile mutabile.
  • numele de tip de bază sunt cele mai compacte pe care le-am întâlnit: i8, i16, i32, i64, u8, u16, u32, u64, f32, f64
  • după cum s-a menționat mai sus, este acceptată inferența de tip automat
Limbajul are instrumente încorporate pentru depanarea programelor:
Cuvânt cheie eșueazăîncheie procesul curent
Cuvânt cheie Buturuga scoate orice expresie de limbă în jurnal (de exemplu, la stderr)
Cuvânt cheie afirma verifică expresia și, dacă este fals, iese din procesul curent
Cuvânt cheie Notă vă permite să afișați informații suplimentare în cazul unei încetări anormale a procesului.

Tipuri de date

Rugina, ca Go, suportă tipărirea structurală(deși, conform autorilor, limbile s-au dezvoltat independent, deci aceasta este influența predecesorilor lor comuni - Alef, Limbo etc.). Ce este tiparea structurală? De exemplu, aveți o structură declarată într-un fișier (sau, în terminologia Rust, „înregistrare”)
tip point = (x: float, y: float);
Puteți declara o mulțime de variabile și funcții cu tipuri de argumente „punct”. Apoi, în altă parte, puteți declara o altă structură, de exemplu
tastați MySuperPoint = (x: float, y: float);
iar variabilele de acest tip vor fi pe deplin compatibile cu variabilele de tip punct.

În schimb, tastarea nominativă utilizată în C, C++, C # și Java nu permite astfel de construcții. Cu tastarea nominativă, fiecare structură este un tip unic, incompatibil cu alte tipuri în mod implicit.

Structurile din Rust sunt numite „înregistrări”. Există și tupluri - acestea sunt aceleași înregistrări, dar cu câmpuri fără nume. Elementele unui tuplu, spre deosebire de elementele unei înregistrări, nu pot fi modificabile.

Există vectori - în unele moduri similari cu tablourile obișnuite și în unele moduri - de tipul vectorial std :: din stl. La inițializarea cu o listă, se folosesc paranteze pătrate, nu paranteze ca în C / C ++

Să myvec =;

Un vector, totuși, este o structură de date dinamică, în special, vectorii suportă concatenarea.

Fie v: mutabil =; v + =;

Există șabloane. Sintaxa lor este destul de logică, fără aglomerația de „șablon” din C++. Sunt acceptate șabloanele de funcție și tipurile de date.

Fn pentru_rev (v: [T], act: bloc (T)) (let i = std :: vec :: len (v); în timp ce i> 0u (i - = 1u; act (v [i]);)) tip circular_buf = (start: uint, end: uint, buf:);

Limba susține așa-numitul Etichete... Aceasta nu este altceva decât o unire din C, cu un câmp suplimentar - codul variantei folosite (adică ceva în comun între unire și enumerare). Sau, teoretic, un tip de date algebrice.

Forma etichetei (cerc (punct, plutitor); dreptunghi (punct, punct);)

În cel mai simplu caz, o etichetă este identică cu o enumerare:

Tag animal (câine; pisică;) let a: animal = câine; a = pisica;
În cazuri mai complexe, fiecare element al „enumerării” este o structură independentă cu propriul „constructor”.
Un alt exemplu interesant este o structură recursivă care definește un obiect de tip „listă”:
lista de etichete (nul; contra (T, @list ); ) lasă a: listă = contra (10, @cons (12, @nil));
Etichetele pot participa la expresii de potrivire a modelelor, care pot fi destul de complexe.
alt x (contra (a, @cons (b, _)) (process_pair (a, b);) contra (10, _) (process_ten ();) _ (eșuare;))

Potrivire de model

Pentru început, puteți considera modelul de potrivire ca un comutator îmbunătățit. Este folosit cuvântul cheie alt, urmat de expresia analizată, iar apoi în corpul operatorului - modele și acțiuni în cazul potrivirii cu modele.
alt numărul_meu (0 (std :: io :: println ("zero"));) 1 | 2 (std :: io :: println ("unul sau doi");) 3 la 10 (std :: io :: println ("de la trei la zece");) _ (std :: io :: println ("altceva");))
Ca „model” puteți folosi nu numai constante (ca în C), ci și expresii mai complexe - variabile, tupluri, intervale, tipuri, substituenți ("_"). Puteți scrie condiții suplimentare folosind clauza when imediat după model. Există o variantă specială a operatorului pentru potrivirea tipului. Acest lucru este posibil deoarece limbajul are un tip de variantă universală. orice ale căror obiecte pot conține valori de orice tip.

Indicatori. Pe lângă indicatoarele „cic” obișnuite, Rust acceptă indicatoare inteligente speciale cu numărătoare de referințe încorporată - Casete partajate și casete Unice. Ele sunt oarecum similare cu shared_ptr și unique_ptr din C ++. Au propria lor sintaxă: @ pentru partajat și ~ pentru unic. Pentru pointerii unici, în loc de copiere, există o operație specială - mutați:
fie x = ~ 10; lasa y<- x;
după o astfel de mutare, indicatorul x este deinițializat.

Închideri, aplicare parțială, iteratoare

Aici începe programarea funcțională. Rust susține pe deplin conceptul de funcții de ordin superior - adică funcții care pot lua drept argumente și pot returna alte funcții.

1. Cuvânt cheie lambda folosit pentru a declara o funcție imbricată sau un tip de date funcțional.

Fn make_plus_function (x: int) -> lambda (int) -> int (lambda (y: int) -> int (x + y)) let plus_two = make_plus_function (2); afirma plus_două (3) == 5;

În acest exemplu, avem o funcție make_plus_care preia un argument „x” de tip int și returnează o funcție de tip „int-> int” (unde lambda este un cuvânt cheie). Corpul funcției descrie chiar această funcție. Absența operatorului „retur” este puțin confuză, totuși, pentru FP acesta este un lucru obișnuit.

2. Cuvânt cheie bloc folosit pentru a declara un tip funcțional - un argument de funcție care poate fi înlocuit cu ceva similar cu un bloc de cod obișnuit.
fn map_int (f: block (int) -> int, vec:) -> (let result =; for i in vec (rezultat + =;) ret result;) map_int ((| x | x + 1),);

Aici avem o funcție cu un bloc ca intrare - de fapt, o funcție lambda de tip „int-> int” și un vector de tip int (mai multe despre sintaxa vectorilor mai târziu). „Blocul” însuși din codul de apel este scris folosind o sintaxă oarecum neobișnuită (| x | x + 1). Personal, îmi place mai mult C # lambdas, | perceput cu încăpățânare ca un SAU pe biți (care, apropo, este și în Rust, ca toate operațiunile vechi C).

3. Aplicarea parțială este crearea unei funcții bazată pe o altă funcție cu un număr mare de argumente prin specificarea valorilor unora dintre argumentele acestei alte funcții. Cuvântul cheie este folosit pentru aceasta. legași substituentul „_”:

Fie daynum = bind std :: vec :: position (_, ["mo", "tu", "we", "do", "fr", "sa", "su"])

Pentru a fi mai clar, voi spune imediat că acest lucru se poate face în C simplu prin crearea unui înveliș simplu, ceva de genul acesta:
const char * daynum (int i) (const char * s = ("mo", "tu", "we", "do", "fr", "sa", "su"); return s [i]; )

Dar aplicarea parțială este un stil funcțional, nu procedural (apropo, nu este clar din exemplul de mai sus cum să faci o aplicație parțială pentru a obține o funcție fără argumente)

Un alt exemplu: funcția add este declarată cu două argumente int, returnând un int. În continuare, este declarat tipul funcțional single_param_fn, care are un argument int și returnează un int. Folosind bind, sunt declarate două obiecte funcție, add4 și add5, construite deasupra funcției add, care are argumente parțiale.

Fn add (x: int, y: int) -> int (ret x + y;) tip single_param_fn = fn (int) -> int; let add4: single_param_fn = bind add (4, _); let add5: single_param_fn = bind add (_, 5);

Obiectele funcționale pot fi apelate în același mod ca și funcțiile obișnuite.
afirma (adăugați (4,5) == adaugă4 (5)); afirma (adăugați (4,5) == adaugă5 (4));

4. Funcții și predicate pure
Funcțiile pure sunt funcții care nu au efecte secundare (inclusiv cele care nu numesc alte funcții decât cele pure). Astfel de funcții se disting prin cuvântul cheie pur.
pur fn lt_42 (x: int) -> bool (ret (x< 42); }
Predicatele sunt funcții pure care returnează un tip bool. Astfel de funcții pot fi folosite în sistemul typestate (vezi mai jos), adică pot fi apelate în timpul compilării pentru diverse verificări statice.

Macro-uri de sintaxă
Funcție planificată, dar foarte utilă. În Rust, este încă în stadiile incipiente de dezvoltare.
std :: io :: println (#fmt ("% s este% d", "răspunsul", 42));
O expresie similară cu printf, dar executată în timpul compilării (în consecință, toate erorile de argument sunt detectate în momentul compilării). Din păcate, există foarte puține materiale despre macrocomenzile sintactice și ele însele sunt în curs de dezvoltare, dar există speranța că ceva de genul macrocomenzilor Nemerle se va dovedi.
Apropo, spre deosebire de același Nemerle, cred că decizia de a evidenția macrocomenzile sintactic folosind simbolul # este foarte competentă: o macro este o entitate care este foarte diferită de o funcție și cred că este important să vedem dintr-o privire unde funcțiile sunt numite în cod și unde - macro-urile.

Atribute

Un concept similar cu atributele C # (și chiar cu sintaxă similară). Mulțumiri speciale dezvoltatorilor pentru acest lucru. După cum v-ați putea aștepta, atributele adaugă meta informații la entitatea pe care o adnotă.
# fn register_win_service () (/ * ... * /)
S-a inventat o altă variantă a sintaxei atributului - aceeași linie, dar cu punct și virgulă la sfârșit, adnotă contextul actual. Adică, orice se potrivește cu acoladele cele mai apropiate care includ un astfel de atribut.
fn register_win_service () (#; / * ... * /)

Calcul paralel

Poate una dintre cele mai interesante părți ale limbii. În același timp, momentan nu este descris deloc în tutorial :)
Un program Rust constă dintr-un „arborele de sarcini”. Fiecare sarcină are o funcție de intrare, propria sa stivă, mijloace de interacțiune cu alte sarcini - canale pentru informații de ieșire și porturi pentru intrare și deține o parte din obiectele din heap-ul dinamic.
Multe sarcini Rust pot exista într-un singur proces de sistem de operare. Sarcinile Rust sunt „ușoare”: fiecare sarcină consumă mai puțină memorie decât un proces de sistem de operare, iar comutarea între ele este mai rapidă decât comutarea între procesele de sistem de operare (aceasta înseamnă probabil „fileuri”).

Sarcina constă din cel puțin o funcție fără argumente. Sarcina este pornită folosind funcția de spawn. Fiecare sarcină poate avea canale prin care comunică informații altor sarcini. Un canal este un tip de șablon special chan, parametrizat de tipul de date al canalului. De exemplu, chan este un canal pentru transferul de octeți nesemnați.
Pentru a trimite către un canal, utilizați funcția de trimitere, al cărei prim argument este canalul, iar al doilea este valoarea care trebuie trimisă. De fapt, această funcție plasează valoarea în bufferul intern al canalului.
Porturile sunt folosite pentru a primi date. Port este un tip de port șablon parametrizat de tipul de date portului: port este portul pentru primirea octeților nesemnați.
Pentru a citi din porturi, se folosește funcția recv, al cărei argument este portul, iar valoarea returnată sunt datele din port. Citirea blochează sarcina, adică. dacă portul este gol, sarcina intră într-o stare în așteptare până când o altă sarcină trimite date pe canalul asociat cu portul.
Legarea canalelor la porturi este foarte simplă - prin inițializarea unui canal cu un port folosind cuvântul cheie chan:
let reqport = port ();
let reqchan = chan (reqport);
Mai multe canale pot fi conectate la un port, dar nu invers - un canal nu poate fi conectat la mai multe porturi în același timp.

Statul de tip

Nu am găsit o traducere general acceptată a conceptului de „state de tip” în rusă, așa că o voi numi „state de tip”. Esența acestei caracteristici este că, pe lângă verificarea obișnuită de tip acceptată în tastarea statică, sunt posibile verificări suplimentare de context în etapa de compilare.
Într-o formă sau alta, stările de tip sunt familiare tuturor programatorilor - conform mesajelor compilatorului, „variabila este folosită fără inițializare”. Compilatorul determină locurile în care o variabilă în care nu a fost niciodată scrisă este utilizată pentru citire și emite un avertisment. Mai general, această idee arată astfel: fiecare obiect are un set de stări pe care le poate lua. În fiecare stare, pentru acest obiect sunt definite operații valide și invalide. Și compilatorul poate verifica dacă o anumită operație asupra unui obiect este permisă într-un anumit loc din program. Este important ca aceste verificări să fie efectuate în timpul compilării.

De exemplu, dacă avem un obiect de tip „fișier”, atunci acesta poate avea starea „închis” și „deschis”. Și operația de citire din fișier este invalidă dacă fișierul este închis. În limbile moderne, este obișnuit ca o funcție de citire fie să arunce o excepție, fie să returneze un cod de eroare. Sistemul de stare de tip ar putea detecta o astfel de eroare în timpul compilării - la fel cum compilatorul stabilește că o operație de citire a unei variabile are loc înainte de orice operație de scriere posibilă, ar putea determina că metoda „Citește”, valabilă în starea „deschis fișierul” , este apelată înaintea metodei „Open”, care transferă obiectul în această stare.

În Rust, există conceptul de „predicate” - funcții speciale care nu au efecte secundare și returnează un tip bool. Astfel de funcții pot fi folosite de compilator pentru a fi apelate în timpul compilării în scopul verificării static a anumitor condiții.

Constrângerile sunt verificări speciale care pot fi efectuate în timpul compilării. Cuvântul cheie check este folosit pentru aceasta.
fn pur este_mai puțin_decât (int a, int b) -< bool { ret a < b; } fn test() { let x: int = 10; let y: int = 20; check is_less_than(x,y); }
Predicatele pot fi „atârnate” de parametrii de intrare ai funcțiilor în felul următor:
test fn (int x, int y): este_mai mic decât (x, y) (...)

Există foarte puține informații despre tipstate, așa că multe puncte sunt încă neclare, dar conceptul este oricum interesant.

Asta e tot. Este posibil să fi ratat câteva puncte interesante, dar articolul era deja umflat. Dacă doriți, puteți construi compilatorul Rust acum și puteți încerca să vă jucați cu diverse exemple. Pentru informații despre asamblare, vezi

Sunt nou în Rust, dar devine rapid limbajul meu de programare preferat. În timp ce scrierea proiectelor mici în Rust este de obicei mai puțin ergonomică și consuma mai mult timp (cel puțin cu mine la volan), aceasta provoacă modul în care mă gândesc la proiectarea programelor. Bătăliile mele cu compilatorul devin mai puțin frecvente după ce învăț ceva nou.

Comunitatea Rust și-a concentrat recent multe eforturi pe I/O asincron, implementat ca biblioteca Tokio. Și asta este grozav.

Mulți dintre membrii comunității, cei care nu au lucrat cu servere web și lucruri conexe, nu au clar ce dorim să obținem. Când s-au discutat aceste lucruri în era 1.0, și eu am avut o idee vagă despre asta, nefiind mai lucrat niciodată cu ele.

  • Ce este - I/O asincron?
  • Ce sunt corutinele ( corutine )?
  • Ce sunt fluxurile ușoare ( fire ușoare )?
  • Ce sunt viitorurile? ( viitoare )?

  • Cum se potrivesc?

Vă voi arăta cum să scrieți un mic program care descarcă banda ( a hrani) în format JSON, analizează și scoate lista de note în consolă în formă formatată.

Totul a dus la un cod foarte laconic. Cum? Uită-te sub tăietură.

Cuvântul cheie nesigur este o parte integrantă a designului limbajului Rust. Pentru cei care nu sunt familiarizați cu acesta: nesigur este un cuvânt cheie care, în termeni simpli, este o modalitate de a ocoli verificarea tipului ( verificarea tipului) Rugina.

Existența cuvântului cheie nesigur este inițial o surpriză pentru mulți. Într-adevăr, nu este o caracteristică a Rust ca programele să nu se blocheze din cauza erorilor de memorie? Dacă da, de ce există o modalitate ușoară de a ocoli sistemul de tip? Acest lucru ar putea părea un defect de design în limbaj.

Cu toate acestea, nesigura nu este un dezavantaj, în opinia mea. De fapt, este o parte importantă a limbii. nesigur acționează ca un fel de supapă de evacuare - asta înseamnă că putem folosi sistemul de tip în cazuri simple, dar ne permite să folosim tot felul de trucuri complicate pe care doriți să le folosiți în codul dvs. Vă solicităm doar să vă ascundeți codul nesigur în spatele abstracțiilor externe sigure.

Această notă introduce cuvântul cheie nesigur și ideea de „nesiguranță” limitată. De fapt, acesta este un prevestitor al unei note pe care sper să o scriu puțin mai târziu. Ea discută despre modelul de memorie Rust, care specifică ce se poate și nu poate fi făcut în cod nesigur.

Ca începător la Rust, am fost confuz cu privire la diferitele moduri de a reprezenta șirurile. Cartea Rust are un capitol numit „Referințe și împrumuturi”, care utilizează trei tipuri diferite de variabile șir în exemple: String, & String și & str.

Să începem cu diferența dintre str și String: String este o structură de date extensibilă, alocată în heap, în timp ce str este un șir imuabil, cu lungime fixă. undeva in minte.

Mulți programatori știu deja să programeze în limbaje orientate pe obiecte. Rust nu este un limbaj clasic orientat pe obiecte, dar instrumentele de bază OOP pot fi aplicate în el.

În acest articol, vom arunca o privire la cum să programați în Rust într-un stil OOP. Vom face acest lucru folosind un exemplu: vom construi o ierarhie de clasă într-o problemă de instruire.

Sarcina noastră este să lucrăm cu forme geometrice. Le vom afișa sub formă de text și le vom calcula aria. Setul nostru de forme - dreptunghi, pătrat, elipsă, cerc.

Rugina este o limbă elegantă care este oarecum diferită de multe alte limbi populare. De exemplu, în loc să folosească clase și moștenire, Rust oferă propriul sistem de tipuri bazat pe trăsături. Cu toate acestea, cred că mulți programatori Rust mai noi (ca mine) nu sunt familiarizați cu modelele de design acceptate.

În acest articol, vreau să discut despre modelul de design tip nou(tip nou), precum și trăsăturile De la și În care ajută la conversia tipului.

M-am gândit mult în ultima vreme la modelele de design și tehnicile pe care le folosim în programare. Este foarte grozav să începi să cercetezi un proiect și să vezi modele și stiluri familiare pe care le-ai întâlnit de mai multe ori. Acest lucru facilitează înțelegerea proiectului și face posibilă accelerarea lucrărilor.

Uneori lucrezi la un proiect nou și realizezi că trebuie să faci ceva în același mod ca în proiectul anterior. Este posibil să nu facă parte din funcționalitate sau dintr-o bibliotecă, poate fi ceva care nu poate fi ambalat într-o macrocomandă sau un container mic. Ar putea fi doar un model de design sau un concept structural care rezolvă bine problema.

Un model interesant aplicat adesea unor astfel de probleme este „Mașina de stat”. Îmi propun să petrecem puțin timp pentru a înțelege ce anume se înțelege prin această frază și de ce este atât de interesantă.

Mai jos este o descriere grafică a mutarii, copierii și împrumutării în limbajul de programare Rust. Practic, aceste concepte sunt specifice doar Rust și sunt adesea o piatră de poticnire pentru începători.

Pentru a evita confuzia, am încercat să păstrez textul la minimum. Această notă nu înlocuiește diverse tutoriale și este făcută doar pentru cei care cred că informațiile sunt percepute vizual mai ușor. Dacă abia începeți să învățați Rust și găsiți aceste grafice utile, atunci aș recomanda să vă marcați codul cu scheme similare pentru a consolida mai bine conceptele.

Implementarea aritmeticii numerelor naturale folosind numerele Peano este o problemă populară în învățarea programarii. Mă întrebam dacă este posibil să le implementez în Rust.

Astfel, sarcina mea este să scriu și să adaug numere naturale cu verificarea tipului.

Potrivit Wikipedia, „axiomele lui Peano sunt unul dintre sistemele de axiome pentru numerele naturale, introduse în secolul al XIX-lea de matematicianul italian Giuseppe Peano”.

Suntem interesați de două dintre ele - cu care puteți introduce și utiliza numere naturale:

  • 1 este un număr natural
  • Numărul care urmează naturalului este de asemenea natural.

Să-l scriem textual să ruginească cu:

1 2 3 4 enum Nat (Zero, Succ (Nat))

Nat este fie zero, fie următorul număr natural.

cometariu: proiectul futures-rs a fost reorganizat și multe lucruri au fost redenumite. Linkurile au fost actualizate acolo unde este posibil.

Începeți cu futures

Acest document vă va ajuta să explorați containerul limbajului de programare Rust, futures, care oferă implementări cu costuri zero pentru futures și fluxuri. Futures sunt disponibile în multe alte limbaje de programare, cum ar fi C++, Java și Scala, iar containerul futures se inspiră din bibliotecile acelor limbaje. Cu toate acestea, este ergonomic și aderă la filozofia de abstractizare cu costuri zero inerentă Rust, și anume că crearea și compunerea futures nu necesită alocări de memorie și este necesară o singură alocare pentru Sarcina care le gestionează. Futures ar trebui să fie coloana vertebrală a I/O composable asincron de înaltă performanță de la Rust, iar benchmark-urile timpurii arată că un server HTTP simplu construit pe futures este foarte rapid.

Această documentație este împărțită în mai multe secțiuni:

  • "Salut Lume!";
  • tip viitor;
  • trăsătura Stream;
  • futures și flux specific;
  • returnarea futures;
  • Sarcina și viitorul;
  • date locale de sarcină.

cometariu: proiectul futures-rs a fost reorganizat și multe lucruri au fost redenumite. Linkurile au fost actualizate acolo unde este posibil.

Unul dintre golurile majore din ecosistemul Rust a fost rapid și eficient I/O asincron... Avem o bază solidă din biblioteca mio, dar este la un nivel foarte scăzut: trebuie să creăm manual mașini de stat și să jonglam cu apelurile.

Ne-am dori ceva de nivel superior, cu o ergonomie mai buna, dar cu buna compozibilitatea prin sprijinirea unui ecosistem de abstracții asincrone care lucrează împreună. Sună foarte familiar: același obiectiv a fost urmărit prin implementare viitoare(sau promite) multor limbi care acceptă zahărul sintactic sub formă asincron / așteaptă deasupra.

Tipurile de numere întregi primitive acceptate de procesoare sunt o aproximare limitată a setului infinit de numere întregi cu care suntem obișnuiți să o operam în viața reală. Această reprezentare limitată nu se potrivește întotdeauna cu numerele „reale”, de exemplu 255_u8 + 1 == 0. Adesea, programatorul uită de această diferență, care poate duce cu ușurință la erori.

Rust este un limbaj de programare care își propune să protejeze împotriva erorilor, se concentrează pe prevenirea celor mai insidioase dintre ele - erorile de memorie, dar încearcă și să ajute programatorul să evite alte probleme: ignorarea erorilor și, după cum vom vedea, depășirile întregi.

Un pic despre Fier

Iron este un cadru web de nivel înalt scris în limbajul de programare Rust și construit pe deasupra unei alte biblioteci hiper notorii. Fierul de călcat este conceput pentru a profita din plin de beneficiile pe care Rust le are de oferit. Fierul încearcă să evite blocarea operațiunilor din miezul său.

Filozofie

Fierul este construit pe principiul expansibilității cât mai mult posibil. El introduce concepte pentru extinderea propriei sale funcționalități:

  • Trăsăturile „intermediare” sunt folosite pentru a implementa funcționalitatea end-to-end în procesarea cererilor;
  • modificatori - folositi pentru a modifica cererile si raspunsurile in cel mai ergonomic mod.

Vă veți familiariza cu partea de bază a modificatorilor și a trăsăturilor intermediare în cursul articolului.

Crearea proiectelor

Mai întâi, să creăm un proiect cu Cargo folosind comanda:

După compilare, obținem fișierul executabil corespunzător:

1 2 3 $ rustc salut.rs $ du -h salut 632K salut

632 kilobytes pentru o imprimare simplă?! Rust este poziționat ca un limbaj de sistem care are potențialul de a înlocui C/C++, nu? Deci, de ce să nu verifici un program similar cu cel mai apropiat concurent al tău?

Se crede larg în mediul nostru că unul dintre avantajele colectorului de gunoi este ușurința de a dezvolta structuri de date de înaltă performanță fără blocare. Gestionarea manuală a memoriei în ele nu este ușoară, iar GC rezolvă cu ușurință această problemă.

Această postare va arăta că folosind Rust este posibil să construiți un API de gestionare a memoriei pentru structuri de date concurente care:

  • Faceți posibilă implementarea unei structuri de date fără blocare, așa cum o face GC;
  • Creați protecție statică împotriva utilizării greșite a schemei de gestionare a memoriei;
  • Va avea costuri generale comparabile cu GC (și mai previzibile).

În testele pe care le voi arăta mai jos, Rust depășește cu ușurință implementările Java de coadă fără blocare, iar implementarea Rust în sine este ușor de scris.

Am implementat o schemă de „recuperare a memoriei pe epocă” în noua bibliotecă Crossbeam, care este acum gata de utilizare cu structurile dumneavoastră de date. În această postare, voi vorbi despre structurile de date fără blocare, algoritmul epoch și API-ul intern Rust.

Erorile de acces la memorie și scurgerile de memorie sunt cele două categorii de erori care atrag cea mai mare atenție, așa că se depune mult efort pentru prevenirea sau cel puțin minimizarea acestora. Deși numele lor sugerează o asemănare, ele sunt într-un fel diametral opuse, iar rezolvarea uneia dintre probleme nu ne eliberează de a doua. Adoptarea pe scară largă a limbilor gestionate dă credință acestei idei: ele previn unele erori de acces la memorie, asumându-și sarcina de a elibera memorie.

Pur și simplu pune: o încălcare a accesului la memorie este un fel de acțiune cu date incorecte, iar o scurgere de memorie este absenta anumite acțiuni cu date corecte... În formă tabelară:

Am câteva gânduri despre învățarea limbajelor de programare.

În primul rând, înțelegem greșit. Sunt sigur că ai trăit același sentiment. Încercați să învățați o nouă limbă și nu prea înțelegeți cum funcționează totul în ea. De ce se folosește o sintaxă într-un loc și alta în altul? Toate aceste ciudățenii sunt enervante și, în final, ne întoarcem la limbajul familiar.

Cred că percepția noastră asupra limbilor ne joacă o glumă crudă. Gândește-te la ultima dată când ai discutat despre o nouă limbă. Cineva a menționat-o, iar altcineva a întrebat despre viteza sa, sintaxa sau cadrul web disponibil.

E ca și cum ai vorbi despre mașini. Ați auzit de noul UAZ Rybak? Cât de repede este? Voi putea să-l călăresc peste lac?

Când vorbim despre limbi într-un mod similar, ne referim la faptul că ele sunt interschimbabile. Ca mașinile. Dacă știu să conduc Lada Saransk, atunci pot conduce UAZ Rybak fără probleme. Singura diferență este în viteză și bord, nu?

Dar imaginați-vă cum ar arăta o mașină PHP. Acum imaginați-vă cât de diferită ar fi o mașină Lisp. Trecerea de la unul la altul va necesita mult mai mult decât să înveți ce buton controlează încălzirea.

Notă: Acest articol presupune că cititorul este familiarizat cu Rust FFI (traducere), endianess și ioctl.

Când creăm legături la codul C, vom întâlni inevitabil o structură care conține o uniune. Rust nu are suport încorporat pentru îmbinări, așa că va trebui să ne definim o strategie. În C, o uniune este un tip care stochează diferite tipuri de date în aceeași zonă de memorie. Există multe motive pentru a alege concatenarea, cum ar fi conversia între reprezentări binare ale numerelor întregi și numere în virgulă mobilă, implementarea pseudo-polimorfismului și accesul direct la biți. Mă voi concentra pe pseudo-polimorfism.