Rugini | |
---|---|
Clasa de limba | limbaj de programare procedural , limbaj de programare functional , limbaj de programare multiparadigma , limbaj de programare imperativ ,de programare sisteme [d] , software gratuit si open source , limbaj de programare compilat si limbaj de programare |
Aparut in | 2006 [1] [5] |
Autor | Corul Graydon [d] |
Dezvoltator | Mozilla [1] , Graydon Hore [d] [1] [2] și Rust Foundation [d] [3] |
Extensie de fișier | .rs |
Eliberare |
|
A fost influențat | Alef [d] [6],C++[7],C#[7],Cyclone[7],Erlang[7],Haskell[7],Limbo[7], Newsqueak [d] ,OCaml[7],Ruby[ 7],Scheme[7],SML[7]șiSwift[7] |
Licență | Licența Apache 2.0 [8] [9] și licența MIT [8] [9] |
Site-ul web | rust-lang.org _ |
OS | multiplatformă |
Fișiere media la Wikimedia Commons |
Rust (Rust, [ rʌst ]; rust din engleză - „rugină”) este un limbaj de programare compilat cu mai multe paradigme de uz general, care combină paradigmele de programare funcționale și procedurale cu un sistem de obiecte bazat pe trăsături . Gestionarea memoriei se realizează prin mecanismul „proprietății” folosind tipuri afine [10] , care vă permite să faceți fără sistemul de colectare a gunoiului în timpul execuției programului. Rust garantează siguranța memoriei cu verificatorul de referințe statice încorporat al compilatorului ( verificatorul de împrumut ). Există instrumente care vă permit să utilizați tehnicile de programare orientată pe obiecte [11] .
Priorități cheie ale limbii: securitate, viteză și concurență . Rust este potrivit pentru programarea sistemelor , în special, este considerat un limbaj promițător pentru dezvoltarea nucleelor de sisteme de operare [10] . Rust este comparabil cu C++ / C în ceea ce privește viteza și caracteristicile , dar oferă mai multă siguranță atunci când lucrați cu memorie, care este oferită de mecanismele de control de referință încorporate în limbaj. Performanța programelor Rust este facilitată de utilizarea „abstracțiilor cu cost zero” [12] .
După câțiva ani de dezvoltare activă, prima versiune stabilă (1.0) a fost lansată pe 15 mai 2015, după care versiuni noi sunt lansate la fiecare 6 săptămâni [13] . Pentru versiunile lingvistice lansate după 1.0, compatibilitatea cu versiunea anterioară este declarată [14] .
Dezvoltat din anii 2010 de Mozilla Research și finanțat de Fundația Mozilla . Începând cu 2020, a fost planificată transferul proprietății intelectuale și proceselor de dezvoltare și finanțare a limbii către Fundația Rust [15] . Pe 8 februarie 2021, cele cinci companii fondatoare ( AWS , Huawei , Google , Microsoft și Mozilla ) au anunțat oficial formarea Fundației Rust. [16] [17]
Timp de șapte ani consecutiv, din 2016 până în 2022, Rust a fost clasat pe locul 1 pe lista „Cele mai iubite limbaje de programare” de sondajul anual Stack Overflow Developer Survey [18] [19] [20] [21] .
Lucrarea la limbă a fost începută de angajatul Mozilla , Graydon Hor , în 2006. Autorul a numit proiectul Rugina, după el, asociat cu ciuperci din familia ruginii ( ing. ciuperci ruginii ) [22] .
În 2009 [23] , Mozilla a început să sponsorizeze separat dezvoltarea Rust. Un an mai târziu, limba a fost prezentată oficial la Mozilla Summit 2010 [24] . Compilatorul original, implementat în OCaml , a fost înlocuit cu unul nou scris în Rust și folosind LLVM pentru a genera codul de mașină [25] ; în anul următor, noul compilator sa compilat cu succes pentru prima dată [26] .
Prima versiune alfa oficială a Rust (0.1) a fost lansată în ianuarie 2012 [27] .
În aprilie 2013, a fost lansat Servo , un proiect experimental al Mozilla pentru a dezvolta un motor de browser în Rust. [28]
Prima versiune stabilă a Rust (1.0) a fost lansată în mai 2015. Interfețele de programare și caracteristicile limbajului au suferit o revizuire semnificativă, după care sunt lăsate implicit doar caracteristicile complet gata de utilizare, a căror implementare nu se va schimba în viitor. Toate celelalte funcții sunt transferate în categoria de experimental și scoase implicit din livrare [29] .
Este folosită tastare statică puternică . Programarea generică este acceptată cu suport pentru polimorfismul parametric , inferența de tip automat este furnizată pentru variabilele locale (dar nu și pentru parametrii funcției).
Suport implementat pentru tipuri de date individuale — tipuri care au exact o instanță și nu ocupă spațiu de memorie, exemple:
Tipuri de date goale implementate — tipuri care nu pot fi instanțiate; implementate ca tipuri enumerate care nu au opțiuni: enum Void {}.
Toate tipurile de date din limbă sunt împărțite în două grupuri principale: tipuri de biblioteci simple și standard.
Tipuri simple (tipuri de lungime constantă încorporate în limbajul în sine) - numeric, boolean, caracter, matrice, felie, felie șir, tuplu, referință, indicator de funcție. Unele dintre tipurile simple sunt „mașină”, adică sunt implementate direct în procesoarele moderne , cum ar fi numerice, booleene și caractere. Tipuri furnizate de biblioteca standard std(lungime variabilă): vector, șir, tabel hash și altele asemenea.
Tipuri numerice:
Boolean ( bool ): true, false.
Caracter ( char ): un tip care reprezintă un caracter Unicode (reprezentare internă a datelor ca u32). Exemple de valori: '₽', '\n', '\x7f', '\u{CA0}',
Indicator de funcție ( indicator de funcție ): obiectele funcție au un tip determinat de semnătura lor, adică de parametri și valoarea returnată. Exemplu:let f: fn(i32) -> i32 = plus_one;
O referință (împrumut comun - împrumut comun ) &T(împrumut comun, nu mutabil, care nu deține o resursă), în loc să preia proprietatea asupra resursei, o împrumută. Numele care împrumută ceva nu eliberează resursa atunci când ies din domeniul de aplicare. În plus, numele proprietarilor intră într-o stare de împrumut.
O referință care este mutabilă (împrumut mutabil ) ( &mut Tnu deține resursa). Vă permite să schimbați resursa care este împrumutată.
Structuri ( struct ):
Colecții :
Enumerare ( enum ): fiecare opțiune dintr-o enumerare în Rust poate fi asociată și cu alte date, motiv pentru care enumerarea este numită și unire etichetată sau tip sumă . Sintaxa pentru declararea variantelor este similară cu sintaxa pentru declararea structurilor: pot exista variante fără date, variante cu date numite și variante cu date fără nume:
Alegerea ar trebui să aibă preferință const, deoarece adesea o constantă nu are nevoie de o anumită adresă în memorie și constvă permite să faceți optimizări precum Constant Folding .
Limbajul implementează un model de gestionare a memoriei axat pe modele de concurență sigure care împiedică accesul incorect la memorie, care este o sursă comună de erori critice de segmentare în alte limbaje de programare. Oferă control asupra utilizării variabilelor neinițializate și deinițializate; este imposibil să împărțiți stări partajate de mai multe sarcini; Este furnizată analiza statică a duratei de viață a pointerilor și verificarea matricei în afara limitelor (automat și întotdeauna, dar este posibil să dezactivați unsafeblocurile de înregistrare folosind metoda get_unchecked).
Așa-numita semantică de mutare este implementată: în mod implicit, Rust „transferă” ( mută ) un pointer către un obiect din heap către un nou proprietar la atribuire, invalidând variabila veche. Acest lucru nu se întâmplă dacă tipul implementează trăsătura Copy deoarece datele de pe stivă sunt copiate.
fie a = „un obiect cu date pe heap” . to_string (); // obiectul trecut la variabila b // variabila a devine neinițializată fie b = a ; // eroare! fie c = a ; // datele obiectului de pe stivă let a = 55 ; // o copie a obiectului este transmisă variabilei b let b = a ; // c = 55 fie c = a ;O altă caracteristică a modelului de memorie este suportul pentru împrumut ( împrumut ) cu capacitatea de a schimba obiectul împrumutat ( &mut) și fără acesta ( &): Lexic și semantic foarte asemănător cu legăturile, dar au specific: împrumutul unui obiect este similar cu semantica lui „ Fie mulți cititori, fie un singur scriitor ” - un obiect poate fi împrumutat fie o dată cu posibilitatea de a schimba obiectul, fie în mod repetat fără el; împrumuturile pot fi reîmprumutate unui alt împrumutat. Spre deosebire de semantica obișnuită „Fie mulți cititori, fie un scriitor”, aceasta nu se aplică în contextul sincronizării firelor, ci universal. Verificarea corectitudinii împrumuturilor are loc în momentul compilării și nu generează cod executabil suplimentar (principiul abstracțiilor cu cost zero ). Compilatorul controlează, de asemenea, raportul dintre duratele de viață ale împrumuturilor și obiectul în sine - împrumuturile nu pot trăi mai mult (depășesc domeniul de aplicare ) al obiectului împrumutat. Împrumuturile funcționează cu orice date, indiferent de locația acesteia (stivă, heap local sau partajat, alte locații speciale). Este necesar să se facă distincția între concepte independente - mutabilitatea împrumutului în sine ( let mut b = &c) și mutabilitatea obiectului împrumutat ( let b = &mut c).
Casetă - Un indicator inteligent care deține un obiect pe heap, distruge obiectul și eliberează memorie atunci când iese din domeniul de aplicare.
Cell ( Cell , RefCell ) implementează mutabilitatea conținutului, în timp ce celula în sine este imuabilă.
Indicatori numărați cu referință ( Rc<T>) și cu referință atomică ( Arc<T>): pointeri inteligente contați cu referință care distrug un obiect și eliberează memorie atunci când contorul este resetat. Arc implementează siguranța firelor pentru numărul de referințe (dar nu și pentru obiectul în sine). Rc și Arc controlează un obiect imuabil, astfel încât utilizarea lor tipică este atât Rc<Cell<T>>într-un program cu un singur fir, cât și Arc<Mutex<T>>într-unul cu mai multe fire.
Pointere brute imuabile ( *const T) și mutabile ( *mut T): Pointere fără garanție de securitate. Nu este recomandat să le folosiți.
Legăturile sunt imuabile în mod implicit și pentru a declara o variabilă mutabilă, aveți nevoie de cuvântul cheie mut .
Exemple:
fie x = 80 ; // leagă proprietarul x la valoarea 80 let mut y = 50 ; // legare mutabilă lat z = & x ; // referință imuabilă la legarea imuabilă let w = & mut y ; // referință imuabilă la legarea mutabilă let r = & mut y ; // eroare: nu se poate crea oa doua referință la o legătură mutabilă * w = 90 // y = 90 * z = 30 // eroare: încercarea de modificare prin referire la o legare imuabilă fie n = Box :: nou ( 42 ); // ambalare let m = Rc :: new ( 55 ); // contor de referință let data = Arc :: new ( " șir_test " ) // contor atomicÎn teza sa de doctorat, Ralph Jung a demonstrat în mod oficial siguranța firului și siguranța managementului memoriei prin utilizarea logicii de partiționare în modelul său RustBelt și instrumentul Iris (bazat pe Coq ) [30] .
Sintaxa limbajului este similară cu C și C++ ; limbajul este sensibil la majuscule, blocurile de cod sunt limitate de acolade; denumirile standard ale structurilor de control dacă sunt folosite , else , while , și for ; comentariile sunt scrise și în format C; Numele modulelor sunt separate prin două caractere două puncte ( ::). Identificatorii pot conține litere latine, cifre și litere de subliniere. Literale șiruri pot folosi orice caracter Unicode UTF-8.
Un set de operatori în Rust: aritmetică ( * - înmulțirea, / - împărțirea, % - luarea restului de împărțire, + - adunarea, - - scăderea și un operator de prefix unar -pentru schimbarea semnului unui număr), pe biți ( >>, <<, &, |și ^), comparație operatori ( ==, !=, <, >, <=, >=), logici ( &&și ||). Rust folosește operatorul binar pentru a turna tipuri as. Turnarea de tip implicit are loc într -un set foarte mic de situații [31] .
Rust acceptă macrocomenzi , substituții de expresii regulate care rulează în timpul fazei de pre-compilare, mai avansate și mai sigure decât C. Macro-urile (macro-urile) sunt extensii de sintaxă simple, definite de utilizator, care pot fi executate cu o comandă macro_rules!. Macro-urile sunt definite în același stil ca și constructul de potrivire a modelului. Atributul macro este un semn de exclamare la sfârșitul numelui. De asemenea, sunt acceptate așa-numitele macrocomenzi „procedurale” [32] care au capacitatea de a executa cod arbitrar în timpul compilării.
Cuvântul cheie letdefinește o legătură (variabilă locală).
fie x : i32 = 5 ;Această notație înseamnă: „ x este o legare de tip i32(întreg de 32 de biți) cu valoarea cinci”.
În limbaj, constructul de potrivire este o versiune generalizată și îmbunătățită a constructului de comutare C. În plus, potrivirea este cel mai puternic, versatil și, s-ar putea chiar spune, elementul cheie de control nu numai pentru fluxul de execuție, ci și pentru structuri de date în limbaj. Se pot potrivi mai multe modele în expresii de potrivire folosind sintaxa |, care înseamnă logic sau.
fie x = 10 ; potrivi cu x { 1 | 2 => println! ( "unu sau doi" ) 3 => println! ( "trei" ) 4 ..= 10 => println! ( "de la patru la zece" ), // Această ramură va funcționa, deoarece 10 aparține acestui interval. _ => println! ( "orice lucru care nu se potrivește cu condițiile de mai sus" ), // "_" se potrivește cu orice valoare }Când lucrați cu tipuri de date compuse (structură, enumerare, tuplu, matrice), le puteți analiza în părți ("destructurare") în interiorul șablonului. Destructurarea structurii:
structPoint { _ x : i32 , y : i32 , } fie punct = Punct { x : 0 , y : 0 }; punct de meci { Punctul { x : 0 , y } => println! ( "x este zero, y este egal cu {}" , y ), // deoarece "x" este egal cu zero, această ramură va funcționa. Punctul { x , y : 0 } => println! ( "x este egal cu {}, y este zero" , x ), Punct { x , y } => println! ( "x = {}, y = {}" , x , y ), }Destructurarea unei enumerari:
culoare enumerare { Rgb ( i32 , i32 , i32 ), hsv ( i32 , i32 , i32 ), } lasă culoare = Culoare :: Hsv ( 0 , 0 , 100 ); potrivește culoarea { Culoare :: Rgb ( 0 , 0 , 0 ) | Culoare :: Hsv ( 0 , 0 , 0 ) => println! ( "negru" ) Culoare :: Rgb ( 255 , 255 , 255 ) | Culoare :: Hsv ( 0 , 0 , 100 ) => println! ( "alb" ), // această ramură va funcționa. Culoare :: Rgb ( roșu , verde , albastru ) => { println! ( "roșu: {}, verde: {}, albastru: {}" , roșu , verde , albastru ) } // va funcționa pentru orice valoare RGB care nu se potrivește cu condițiile de mai sus. Culoare :: Hsv ( nuanță , saturație , luminozitate ) => println! ( „nuanță: {}, saturație: {}, luminozitate: {}” , nuanță , saturație , luminozitate ), // la fel, dar cu Hsv. }Destructurarea tuplurilor:
fie ( a , b ) = ( 1 , 2 ); println! ( "{}" , a ); // 1 println! ( "{}" , b ); // 2Sintaxa if letvă permite să combinați ifși letîntr-un construct mai puțin pronunțat și apoi să procesați valorile corespunzătoare unui singur model, ignorând toate celelalte. Această sintaxă este adecvată atunci când trebuie să se potrivească doar un model.
fie x = Unii ( 10 ); dacă fie Some ( valoare ) = x { // aici destructuram x, valoarea variabila stocheaza valoarea 10. // aceasta ramura va fi executata, deoarece "x" stocheaza valoarea in interior. println! ( "valoare = {}" , valoare ); } altfel { // operatorul „else” de aici acționează ca un înlocuitor pentru „_” în expresiile de potrivire. println! ( "x - gol" ); }În blocurile și funcțiile marcate cu unsafe( unsafe din engleză - „unsafe”), compilatorul vă permite să faceți doar cinci lucruri suplimentare:
Trebuie unsafesă recurgeți la crearea abstracțiilor de nivel scăzut, în special atunci când dezvoltați biblioteca standard Rust; codul normal este recomandat să fie scris fără unsafe.
În Rust, sistemul de obiecte se bazează pe trăsături ( trăsături ) și structuri ( structuri ). Trăsăturile definesc semnăturile metodei care trebuie implementate pentru fiecare tip (cel mai adesea o structură) care implementează trăsătura. O trăsătură poate conține și implementări implicite ale metodelor. Implementarea trăsăturilor pentru o anumită structură, precum și implementarea metodelor proprii ale structurii, se notează prin cuvântul cheie impl. Limbajul conține câteva zeci de trăsături încorporate, dintre care majoritatea sunt folosite pentru supraîncărcarea operatorului , iar unele au o semnificație specială.
Rust susține analogia moștenirii trăsăturilor - o trăsătură poate necesita un tip de implementare pentru a implementa alte trăsături. Cu toate acestea, nu există suport lingvistic pentru moștenirea tipurilor în sine și, prin urmare , OOP clasic , în Rust. În loc de moștenirea tipului, analogia ierarhiei de clasă este implementată prin introducerea de trăsături, inclusiv o structură strămoșească în cadrul unei structuri copil, sau introducerea de enumerații pentru a generaliza diferite structuri [33] .
Limbajul acceptă tipuri generice ( generice ). Pe lângă funcții, Rust poate generaliza și tipuri de date complexe, structuri și enumerari . Compilatorul Rust compilează funcții generice foarte eficient prin monomorfizarea lor (generând o copie separată a fiecărei funcție generice direct la fiecare punct de apel). Astfel, copia poate fi adaptată la tipuri specifice de argumente și, prin urmare, optimizată pentru aceste tipuri. În acest sens, funcțiile generice ale Rust sunt comparabile ca performanță cu șabloanele de limbaj C++ .
Versiunile anterioare ale limbajului acceptau fire de execuție ușoare, dar acestea au fost abandonate în favoarea firelor de execuție a sistemului de operare nativ . Cu toate acestea, metoda recomandată pentru schimbul de date între fire este de a trimite mesaje, mai degrabă decât de a folosi memoria partajată. Pentru a obține performanțe ridicate, este posibil să trimiteți date nu prin copiere, ci folosind propriile pointeri ( Box<T>). Acestea garantează un singur proprietar.
Definirea și invocarea operațiilor asincrone sunt suportate la nivel de sintaxă a limbajului: un cuvânt cheie asyncdefinește o funcție sau un bloc asincron; un apel normal la o astfel de funcție returnează un obiect cu o trăsătură Future — un mâner la o operație asincronă leneșă [34] . Apelul .awaitpermite unei operații asincrone să aștepte până la finalizarea unei alte operații asincrone. În același timp, implementarea mediului de execuție pentru operații asincrone nu este inclusă nici în nucleul limbajului, nici în biblioteca standard, ci este asigurată de biblioteci terțe [35] .
Sistem de module: o unitate de compilare („ladă”) poate consta din mai multe module. Ierarhia modulelor se potrivește de obicei cu ierarhia directoarelor și fișierelor de proiect. Un modul (de regulă) este un fișier separat și este, de asemenea, un spațiu de nume și unul dintre mijloacele de control al vizibilității identificatorilor: în cadrul modulului (și în submodule) toți identificatorii sunt „vizibili”, în modulele superioare doar publici ( pub) funcții, tipuri, trăsături, constante, submodule, câmpuri ale structurilor.
Testare automată: limbajul face posibilă implementarea testelor unitare automate (testuri unitare) direct în modulul sau submodulul testat. Metodele de testare sunt ignorate în timpul compilării și sunt apelate numai în timpul testării. Testele de integrare sunt implementate ca lăzi separate în tests.
Documentare automată: Instrumentul rustdoc vă permite să generați documentație HTML direct din codul sursă. Documentația din cod este marcată cu o bară oblică triplă ( /// Пример документации) sau o bară oblică dublă cu un semn de exclamare, pentru documentația modulului - ( //! Пример документации модуля). Limbajul de marcare Markdown este acceptat . Codul care este executabil (testele de documentație) poate fi încorporat în documentație. Acest lucru permite, printre altele, să se verifice relevanța documentației atunci când se efectuează modificări în proiect.
Sistem de gestionare a pachetelor: managerul de pachete de marfă (care este și instrumentul principal pentru crearea, compilarea și testarea proiectelor) folosind fișierul manifest Cargo. toml rezolvă dependențele de proiect cuzile importate) prin descărcarea lor din depozitul crates.io .
Cerințe pentru identificatori: compilatorul controlează implementarea convențiilor de denumire pentru variabile, tipuri, funcții și așa mai departe ( snake_case , UpperCamelCase , SCREAMING_SNAKE_CASE), precum și identificatorii neutilizați; se recomandă ca identificatorii neutilizați să înceapă cu un caracter de subliniere; există anumite linii directoare pentru denumirea constructorilor, metode de conversie a tipurilor etc. [36]
Principiile de gestionare a memoriei Rust sunt semnificativ diferite de ambele limbi cu acces complet la memorie și limbi cu control complet al memoriei de către colectorul de gunoi . Modelul de memorie al lui Rust este construit în așa fel încât, pe de o parte, oferă dezvoltatorului capacitatea de a controla unde să aloce datele, introducând separarea pe tipuri de pointeri și oferind control asupra utilizării acestora în etapa de compilare. Pe de altă parte, mecanismul de numărare a referințelor Rust tinde să arunce erori de compilare în cazurile în care utilizarea altor limbi are ca rezultat erori de rulare sau blocări ale programului.
Limbajul vă permite să declarați funcții și blocuri de cod ca „nesigure” ( unsafe). Unele restricții nu se aplică în domeniul de aplicare a unui astfel de cod nesigur, deci este posibil să se efectueze operațiuni la un nivel inferior, dar dezvoltatorul trebuie să înțeleagă pe deplin ce face.
Mozilla | Proiecte|
---|---|
Browsere | |
Alte proiecte | |
Nu se dezvoltă | |
Infrastructură | |
Componente |
|