Rost | |
---|---|
Språkklass | procedurmässigt programmeringsspråk , funktionellt programmeringsspråk , multiparadigm programmeringsspråk , imperativt programmeringsspråk , systemprogrammeringsspråk [d] , fri och öppen källkod , kompilerat programmeringsspråk och programmeringsspråk |
Framträdde i | 2006 [1] [5] |
Författare | Graydon Choir [d] |
Utvecklaren | Mozilla [1] , Graydon Hore [d] [1] [2] och Rust Foundation [d] [3] |
Filtillägg _ | .rs |
Släpp | |
Blivit påverkad | Alef [d] [6],C++[7],C#[7],Cyclone[7],Erlang[7],Haskell[7],Limbo[7], Newsqueak [d] ,OCaml[7],Ruby[ 7],Schema[7],SML[7]ochSwift[7] |
Licens | Apache License 2.0 [8] [9] och MIT License [8] [9] |
Hemsida | rust-lang.org _ |
OS | plattformsoberoende |
Mediafiler på Wikimedia Commons |
Rust (Rust, [ rʌst ]; rust från engelska - "rust") är ett multiparadigmkompilerat programmeringsspråk för allmänt bruk som kombinerar funktionella och procedurmässiga programmeringsparadigm med ett objektsystem baserat på egenskaper . Minneshantering utförs genom mekanismen för "ägande" med affina typer [10] , vilket låter dig klara dig utan sophämtningssystemet under programkörning. Rust garanterar minnessäkerhet med kompilatorns inbyggda statiska referenscheckare ( lånecheckare ). Det finns verktyg som låter dig använda teknikerna för objektorienterad programmering [11] .
Viktiga språkprioriteringar: säkerhet, hastighet och samtidighet . Rust är lämpligt för systemprogrammering , i synnerhet anses det vara ett lovande språk för att utveckla operativsystemkärnor [10] . Rust är jämförbar med C++ / C när det gäller hastighet och funktioner , men ger mer säkerhet när man arbetar med minne, vilket tillhandahålls av referenskontrollmekanismerna inbyggda i språket. Prestanda för Rust-program underlättas av användningen av "nollkostnadsabstraktioner" [12] .
Efter flera år av aktiv utveckling släpptes den första stabila versionen (1.0) den 15 maj 2015, varefter nya versioner släpps var 6:e vecka [13] . För språkversioner släppta efter 1.0 deklareras bakåtkompatibilitet [14] .
Utvecklat sedan 2010-talet av Mozilla Research och finansierat av Mozilla Foundation . Från och med 2020 var det planerat att överföra de immateriella rättigheterna och utvecklings- och finansieringsprocesserna för språket till Rust Foundation [15] . Den 8 februari 2021 tillkännagav de fem grundande företagen ( AWS , Huawei , Google , Microsoft och Mozilla ) officiellt bildandet av Rust Foundation. [16] [17]
Under sju år i rad från 2016 till 2022 har Rust rankats som nummer 1 på listan över "Most älskade programmeringsspråk" av den årliga Stack Overflow Developer Survey [18] [19] [20] [21] .
Arbetet med språket startades av Mozilla -medarbetaren Graydon Hor 2006. Författaren döpte projektet till Rust, enligt honom, förknippat med svampar av rostfamiljen ( eng. rust svampar ) [22] .
2009 [23] började Mozilla separat sponsra utvecklingen av Rust. Ett år senare presenterades språket officiellt vid Mozilla Summit 2010 [24] . Den ursprungliga kompilatorn, implementerad i OCaml , har ersatts med en ny skriven i Rust och använder LLVM för att generera maskinkod [25] ; följande år kompilerade den nya kompilatorn sig själv för första gången [26] .
Den första officiella alfaversionen av Rust (0.1) släpptes i januari 2012 [27] .
I april 2013 lanserades Servo , ett experimentprojekt av Mozilla för att utveckla en webbläsarmotor i Rust. [28]
Den första stabila versionen av Rust (1.0) släpptes i maj 2015. Programmeringsgränssnitten och språkfunktionerna har genomgått en betydande revidering, varefter endast helt färdiga att använda funktioner finns kvar som standard, vars implementering inte kommer att förändras i framtiden. Alla andra funktioner överförs till kategorin experiment och tas ur leverans som standard [29] .
Stark statisk typning används . Generisk programmering stöds med stöd för parametrisk polymorfism , automatisk typinferens tillhandahålls för lokala variabler (men inte för funktionsparametrar).
Implementerat stöd för enstaka datatyper — typer som har exakt en instans och inte tar upp minnesutrymme, exempel:
Implementerade tomma datatyper — typer som inte kan instansieras; implementeras som uppräknade typer som inte har några alternativ: enum Void {}.
Alla datatyper i språket är indelade i två huvudgrupper: enkla och vanliga bibliotekstyper.
Enkla typer (typer av konstant längd inbyggda i själva språket) - numeriska, booleska, tecken, array, segment, strängsnitt, tuppel, referens, funktionspekare. Några av de enkla typerna är "maskin", det vill säga de implementeras direkt i moderna processorer , såsom numerisk, boolean och tecken. Typer som tillhandahålls av standardbiblioteket std(variabel längd): vektor, sträng, hashtabell och liknande.
Numeriska typer:
Boolean ( bool ): true, false.
Character ( char ): En typ som representerar ett Unicode-tecken (intern datarepresentation som u32). Exempelvärden: '₽', '\n', '\x7f', '\u{CA0}',
Funktionspekare ( funktionspekare ): Funktionsobjekt har en typ som bestäms av sin signatur, det vill säga parametrar och returvärde. Exempel:let f: fn(i32) -> i32 = plus_one;
En referens (delad lån - delad lån ) &T(delad, inte föränderlig, inte äga en resurs), istället för att ta äganderätten till resursen, lånar den den. Namn som lånar något frigör inte resursen när de går utanför räckvidden. Dessutom går ägarnamnen i ett lånat tillstånd.
En referens som är föränderlig (föränderlig lån ) ( &mut Täger inte resursen). Låter dig ändra resursen som lånas.
Strukturer ( struct ):
Uppräkning ( enum ): varje alternativ i en uppräkning i Rust kan också associeras med annan data, varför uppräkningen också kallas för en taggad union eller summatyp . Syntaxen för att deklarera varianter liknar syntaxen för att deklarera strukturer: det kan finnas varianter utan data, varianter med namngivna data och varianter med namnlösa data:
Valet bör ges företräde const, eftersom en konstant ofta inte behöver en specifik adress i minnet och constlåter dig göra optimeringar som Constant Folding .
Språket implementerar en minneshanteringsmodell fokuserad på säkra samtidighetsmönster som förhindrar felaktig minnesåtkomst, vilket är en vanlig källa till kritiska segmenteringsfel i andra programmeringsspråk. Ger kontroll över användningen av oinitierade och avinitierade variabler; det är omöjligt att dela delade tillstånd med flera uppgifter; statisk analys av livslängden för pekare och kontroll av out-of-bounds-array tillhandahålls (automatiskt och alltid, men det är möjligt att stänga av incheckningsblocken unsafemed metoden get_unchecked).
Den så kallade Move-semantiken är implementerad: som standard "överför" ( flytta ) Rust en pekare till ett objekt på högen till en ny ägare vid tilldelning, vilket ogiltigförklarar den gamla variabeln. Detta händer inte om typen implementerar Copy-egenskapen eftersom data på stacken kopieras.
låt a = "ett objekt med data på högen" . till_sträng (); // objekt som skickas till variabel b // variabel a blir oinitierad låt b = a ; // fel! låt c = a ; // objektdata på stacken låt a = 55 ; // en kopia av objektet skickas till variabel b låt b = a ; // c = 55 låt c = a ;En annan egenskap hos minnesmodellen är stöd för att låna ( låna ) med möjligheten att ändra det lånade objektet ( &mut) och utan det ( &): Lexiskt och semantiskt mycket lik länkar, men har specifika egenskaper: att låna ett objekt liknar semantiken för " Antingen många läsare, eller en författare " - ett föremål kan lånas antingen en gång med möjlighet att ändra föremålet, eller upprepade gånger utan det; lån kan återlånas till annan låntagare. Till skillnad från den vanliga "Antingen många läsare eller en skribent"-semantik, gäller den inte i samband med trådsynkronisering, utan universellt. Kontroll av att lånen är korrekta sker vid kompilering och genererar ingen ytterligare exekverbar kod (principen om nollkostnadsabstraktioner ). Kompilatorn kontrollerar också förhållandet mellan lånens livslängd och själva objektet - lån kan inte leva längre (gå utanför räckvidden ) för det lånade objektet. Lån fungerar med all data oavsett var den befinner sig (stack, lokal eller delad hög, andra speciella platser). Det är nödvändigt att skilja mellan oberoende begrepp - föränderligheten av själva upplåningen ( let mut b = &c) och föränderligheten av det lånade objektet ( let b = &mut c).
Box - En smart pekare som äger ett objekt på högen, förstör objektet och frigör minne när det går utanför räckvidden.
Cell ( Cell , RefCell ) implementerar innehållsföränderlighet medan själva cellen är oföränderlig.
Referensräknade ( Rc<T>) och atomreferensräknade ( Arc<T>) pekare: Referensräknade smarta pekare som förstör ett objekt och frigör minne när räknaren återställs. Arc implementerar trådsäkerhet för referensräkningen (men inte för själva objektet). Rc och Arc styr ett oföränderligt objekt, så deras typiska användning är både Rc<Cell<T>>i ett entrådigt program och Arc<Mutex<T>>i ett flertrådigt.
Råpekare oföränderliga ( *const T) och föränderliga ( *mut T): Pekare utan säkerhetsgaranti. Det rekommenderas starkt inte att använda dem.
Bindningar är oföränderliga som standard, och för att förklara en variabel föränderlig behöver du nyckelordet mut .
Exempel:
låt x = 80 ; // bind ägare x till värde 80 låt mut y = 50 ; // föränderlig bindning låt z = & x ; // oföränderlig referens till oföränderlig bindning låt w = & mut y ; // oföränderlig referens till föränderlig bindning låt r = & mut y ; // fel: kan inte skapa en andra referens till en föränderlig bindning * w = 90 // y = 90 * z = 30 // fel: Försök att ändra via referens till en oföränderlig bindning låt n = Box :: new ( 42 ); // packning låt m = Rc :: new ( 55 ); // referensräknare låt data = Arc :: new ( "test_string" ) // atomräknareI sin doktorsavhandling bevisade Ralph Jung formellt trådsäkerheten och säkerheten för minneshantering genom att använda partitioneringslogik i sin RustBelt-modell och Iris-verktyget (baserat på Coq ) [30] .
Språkets syntax liknar C och C++ ; språket är skiftlägeskänsligt, kodblock begränsas av lockiga hängslen; standardnamnen på kontrollstrukturer om , else , while och for används ; kommentarer skrivs även i C-format; modulnamn separeras med två kolon ( ::). Identifierare kan innehålla latinska bokstäver, siffror och understreck. Strängliteraler kan använda vilket UTF-8 unicode-tecken som helst.
En uppsättning operatorer i Rust: aritmetik ( * - multiplikation, / - division, % - tar resten av division, + - addition, - - subtraktion och en unär prefixoperator -för att ändra tecknet för ett tal), bitvis ( >>, <<, &, |och ^), jämförelse operatorer ( ==, !=, <, >, <=, >=), logiska ( &&och ||). Rust använder den binära operatorn för att gjuta typer as. Implicit typgjutning förekommer i en mycket liten uppsättning situationer [31] .
Rust stöder makron , ersättningar för reguljära uttryck som körs under förkompileringsfasen, mer avancerade och säkrare än C. Makron (makron) är användardefinierade, enkla syntaxtillägg som kan köras med ett kommando macro_rules!. Makron definieras i samma stil som mönstermatchningskonstruktionen. Makroattributet är ett utropstecken i slutet av namnet. Dessutom stöds så kallade "procedurmässiga" makron [32] som har förmågan att exekvera godtycklig kod vid kompilering.
Nyckelordet letdefinierar en bindning (lokal variabel).
låt x : i32 = 5 ;Denna notation betyder: " x är en typbindning i32(32-bitars heltal) med värde fem".
På språket är matchningskonstruktionen en generaliserad och förbättrad version av C-switchkonstruktionen. Dessutom är matchning den mest kraftfulla, mångsidiga och, man kan till och med säga, nyckelkontrollelementet inte bara för flödet av exekvering, utan också för datastrukturer i språket. Flera mönster kan matchas i matchningsuttryck med syntaxen |, vilket betyder logisk eller.
låt x = 10 ; matcha x { 1 | 2 => println! ( "en eller två" ), 3 => println! ( "tre" ) 4 ..= 10 => println! ( "från fyra till tio" ) // Den här grenen kommer att fungera, eftersom 10 tillhör detta intervall. _ => println! ( "allt som inte matchar villkoren ovan" ), // "_" matchar alla värden }När du arbetar med sammansatta datatyper (struktur, uppräkning, tupel, array) kan du analysera dem i delar ("destrukturera") inuti mallen. Strukturförstöring:
structPoint { _ x : i32 , y : i32 , } låt punkt = Punkt { x : 0 , y : 0 }; match punkt { Punkt { x : 0 , y } => println! ( "x är noll, y är lika med {}" , y ), // eftersom "x" är lika med noll, kommer denna gren att fungera. Punkt { x , y : 0 } => println! ( "x är lika med {}, y är noll" , x ), Punkt { x , y } => println! ( "x = {}, y = {}" , x , y ), }Destrukturera en uppräkning:
enum färg { Rgb ( i32 , i32 , i32 ), hsv ( i32 , i32 , i32 ), } låt färg = Färg :: Hsv ( 0 , 0 , 100 ); matcha färg { Färg :: Rgb ( 0 , 0 , 0 ) | Färg :: Hsv ( 0 , 0 , 0 ) => println! ( "svart" ) Färg :: Rgb ( 255 , 255 , 255 ) | Färg :: Hsv ( 0 , 0 , 100 ) => println! ( "vit" ), // den här grenen kommer att fungera. Färg :: Rgb ( röd , grön , blå ) => { println! ( "röd: {}, grön: {}, blå: {}" , röd , grön , blå ) } // kommer att fungera för alla Rgb-värden som inte matchar villkoren ovan. Färg :: Hsv ( nyans , mättnad , ljusstyrka ) => println! ( "nyans: {}, mättnad: {}, ljusstyrka: {}" , nyans , mättnad , ljusstyrka ), // detsamma, men med Hsv. }Tuple-destrukturering:
låt ( a , b ) = ( 1 , 2 ); println! ( "{}" , a ); // 1 println! ( "{}" , b ); // 2Syntaxen if letlåter dig kombinera ifoch lettill en mindre utförlig konstruktion och sedan bearbeta värdena som motsvarar endast ett mönster, samtidigt som du ignorerar alla andra. Denna syntax är lämplig när endast ett mönster behöver matchas.
låt x = Vissa ( 10 ); om låt Some ( värde ) = x { // här destrukturerar vi x, variabelvärdet lagrar värdet 10. // denna gren kommer att exekveras, eftersom "x" lagrar värdet inuti. println! ( "värde = {}" , värde ); } annat { // "else"-operatorn här fungerar som en ersättning för "_" i matchningsuttryck. println! ( "x - tom" ); }I block och funktioner markerade med unsafe( osäkra från engelska - "osäker"), låter kompilatorn dig göra endast fem ytterligare saker:
Du unsafemåste tillgripa när du skapar abstraktioner på låg nivå, särskilt när du utvecklar Rust-standardbiblioteket; normal kod rekommenderas att skrivas utan unsafe.
I Rust bygger objektsystemet på egenskaper ( traits ) och strukturer ( structs ). Egenskaper definierar metodsignaturer som måste implementeras för varje typ (oftast en struktur) som implementerar egenskapen. En egenskap kan också innehålla standardimplementationer av metoder. Implementeringen av egenskaper för en given struktur, liksom implementeringen av strukturens egna metoder, betecknas med nyckelordet impl. Språket innehåller flera dussin inbyggda egenskaper, varav de flesta används för operatörsöverbelastning , och några av dem har en speciell betydelse.
Rust stödjer egenskapens arvsanalogi - en egenskap kan kräva en implementerande typ för att implementera andra egenskaper. Det finns dock inget språkstöd för nedärvning av själva typerna, och därmed klassisk OOP , i Rust. Istället för typarv, implementeras klasshierarkianalogin genom att introducera egenskaper, inklusive en förfaderstruktur inom en barnstruktur, eller införa uppräkningar för att generalisera olika strukturer [33] .
Språket stöder generiska typer ( generics ). Förutom funktioner kan Rust även generalisera komplexa datatyper, strukturer och enums . Rust -kompilatorn kompilerar generiska funktioner mycket effektivt genom att monomorfisera dem (genererar en separat kopia av varje generisk funktion direkt vid varje anropspunkt). Således kan kopian anpassas till specifika typer av argument, och därför optimeras för dessa typer. I detta avseende är Rusts generiska funktioner jämförbara i prestanda med C++-språkmallar .
Tidigare versioner av språket stödde lätta trådar , men de övergavs till förmån för inbyggda operativsystemtrådar . Den rekommenderade metoden för att utbyta data mellan trådar är dock att skicka meddelanden istället för att använda delat minne. För att uppnå hög prestanda är det möjligt att skicka data inte genom kopiering, utan genom att använda egna pekare ( Box<T>). De garanterar endast en ägare.
Definitionen och anropandet av asynkrona operationer stöds på språksyntaxnivå: ett nyckelord asyncdefinierar en asynkron funktion eller ett asynkront block; ett normalt anrop till en sådan funktion returnerar ett objekt med en egenskap Future — ett handtag till en lat asynkron operation [34] . Samtalet .awaittillåter en asynkron operation att vänta tills en annan asynkron operation har slutförts. Samtidigt ingår inte implementeringen av exekveringsmiljön för asynkrona operationer vare sig i kärnan av språket eller i standardbiblioteket, utan tillhandahålls av tredjepartsbibliotek [35] .
Modulsystem: en kompileringsenhet ("låda") kan bestå av flera moduler. Modulhierarkin matchar vanligtvis hierarkin av kataloger och projektfiler. En modul (som regel) är en separat fil, och är också ett namnområde och ett av sätten att kontrollera synligheten av identifierare: inom modulen (och i undermoduler) är alla identifierare "synliga", i högre moduler endast offentliga ( pub) funktioner, typer, egenskaper, konstanter, undermoduler, strukturfält.
Automatiserad testning: språket gör det möjligt att implementera automatiserade enhetstester (enhetstester) direkt i den modul eller undermodul som testas. Testmetoder ignoreras under kompilering och anropas endast under testning. Integrationstester implementeras som separata lådor i tests.
Automatiserad dokumentation: Rustdoc- verktyget låter dig skapa HTML-dokumentation direkt från källkoden. Dokumentation i koden är markerad med ett trippelt snedstreck ( /// Пример документации) eller ett dubbelt snedstreck med ett utropstecken, för moduldokumentation - ( //! Пример документации модуля). Markup-språket Markdown stöds . Kod som är körbar (dokumentationstester) kan bäddas in i dokumentationen. Detta gör det bland annat möjligt att kontrollera dokumentationens relevans vid ändringar i projektet.
Pakethanteringssystem: lastpakethanterare ( som också är huvudverktyget för att skapa, sammanställa och testa projekt) med hjälp av Cargo-manifestfilen. toml löser projektberoenden importerade backar) genom att ladda ner dem från crates.io- förvaret .
Krav på identifierare: kompilatorn styr implementeringen av namnkonventioner för variabler, typer, funktioner och så vidare ( snake_case , UpperCamelCase , SCREAMING_SNAKE_CASE), såväl som oanvända identifierare; oanvända identifierare rekommenderas att börja med ett understreck; det finns vissa riktlinjer för namngivning av konstruktörer, typkonverteringsmetoder etc. [36]
Rusts minneshanteringsprinciper skiljer sig markant från båda språken med full minnesåtkomst och språk med full minneskontroll av sopsamlaren . Rusts minnesmodell är byggd på ett sådant sätt att den å ena sidan ger utvecklaren möjligheten att styra var data ska allokeras, införa separation efter pekartyper och ge kontroll över deras användning i kompileringsstadiet. Å andra sidan tenderar Rusts referensräkningsmekanism att kasta kompileringsfel i fall där användningen av andra språk resulterar i körtidsfel eller programkrascher.
Språket låter dig förklara funktioner och kodblock som "osäkra" ( unsafe). Vissa begränsningar gäller inte inom omfattningen av sådan osäker kod, så det är möjligt att utföra operationer på en lägre nivå, men utvecklaren måste helt förstå vad han gör.
Mozilla -projekt | |
---|---|
Webbläsare | |
Andra projekt | |
Utvecklas inte | |
Infrastruktur | |
Komponenter |
|