- Rust säkerställer minnessäkerhet vid kompilering genom ägande, lån och livslängder, utan att använda sophämtning.
- Typsystemet och aliasingreglerna tillåter samtidighet utan dataraces med hjälp av mutexer, kanaler och smarta pekare.
- Cargo, crates.io och ett aktivt ekosystem förenklar beroendehantering, kompilering, testning och driftsättning.
- Att förstå strukturer, enumer, alternativ och resultat är nyckeln till att hantera fel och modellera säkra data i samtidiga applikationer.
Rost har blivit ett av de språk som Varje systemutvecklare får höra det om och om igen.Det är lika snabbt som C och C++, men med ett nästan besatt fokus på minnessäkerhet och väl utförd samtidighet. Detta är inte bara tom marknadsföring: dess design kretsar kring att kompilatorn upptäcker fel vid kompileringstid – fel som man i andra språk bara ser när systemet redan är i produktion… eller när det kraschar.
Om du är intresserad av att förstå Hur Rust uppnår säkert minne utan sophämtning och samtidighet utan rädsla för datakörningarDen här handledningen är för dig. Vi kommer att täcka allt från språkets grunder och dess ekosystem till viktiga begrepp som ägande, lån, sammansatta typer, verktyg som Cargo, och till och med titta på atomtyper och låsning ur ett mer lättillgängligt perspektiv för de som är nya inom samtidighet, allt med fokus på säkerhet och prestanda.
Rust-handledning: Prestanda, minnessäkerhet och samtidighet
Rust är ett programmeringsspråk programmering allmänt ändamål och multiparadigm, utformad för lågnivåsystemprogrammering såväl som för projekt på hög nivå, från OSFrån spelmotorer och webbläsare till högpresterande webbtjänster, det har sitt ursprung i Mozilla med målet att förbättra programvarusäkerheten, särskilt i känsliga komponenter som en webbläsarmotor.
Dess utmärkande kännetecken är att garanterar minnessäkerhet vid kompileringstid utan att använda en sophämtare. Istället använder Rust ett ägarskapssystem och en lånekontroll som spårar livslängden för varje värde och dess referenser. Detta undviker klassiska problem som dinglande pekare, buffertöverflöden eller minnesläckor utan att kräva automatisk referensräkning eller sophämtning.
Dessutom är Rust utformad för att göra det enklare säker samtidighetDess typ- och ägarmodell förhindrar datakapplöpningar mellan trådar, åtminstone medan den förblir i säker Rust-kod. Detta innebär att många farliga situationer upptäcks vid kompileringstillfället, innan en enda rad exekveras.
Av alla dessa skäl, stora företag som Dropbox, Microsoft, Amazon eller Google De har anammat Rust i kritiska delar av sin infrastruktur. Och det är ingen slump att det har toppat Stack Overflow-omröstningarna i åratal som ett av de "mest älskade" språken av utvecklare: det kombinerar prestanda i C++-stil med en modern verktygsuppsättning (Cargo, crates.io) och en mycket aktiv community, de så kallade Rustaceans.
Grundläggande begrepp: programmeringsspråk, typer och minne
Innan vi går in på detaljerna kring minnessäkerhet och samtidighet är det värt att förtydliga några allmänna begrepp som förekommer genomgående. el tiempo När man arbetar med rost, särskilt om du kommer från andra språk eller precis har börjat programmera.
Ett programmeringsspråk är i slutändan en uppsättning regler och strukturer som låter dig beskriva algoritmer och omvandla dem till körbara program. Rust kompilerar till maskinkod med hjälp av sin kompilator. rustcDärför är prestandan du får vanligtvis i nivå med C och C++.
Minneshantering är den process genom vilken ett program reserverar och frigör minnesblock under körningFel inom detta område är ofta dödliga: minnesläckor (misslyckande med att frigöra oanvänt minne), datakorruption från skrivning utanför gränserna eller användning av minne efter att det har frigjorts. Rust åtgärdar detta med ett mycket starkt typsystem och formella regler för ägande, lån och livslängder.
Rost har även termer som smarta typer och pekareEn typ beskriver vilken typ av data en variabel lagrar (heltal, flyttal, strängar, strukturer etc.) och hur den kan manipuleras. Smarta pekare (till exempel Box, Rc y Arc) är strukturer som inkapslar minnesadresser och lägger till extra logik för att hantera resurser på ett säkert sätt, till exempel att räkna delade referenser eller flytta värden till heapen.
Inom konkurrensområdet används begrepp som tävlingsvillkor, mutexer och kanaler De blir oumbärliga: ett kapplöpningsvillkor uppstår när flera trådar samtidigt öppnar och modifierar en delad resurs utan ordentlig samordning; en mutex (ömsesidig uteslutning) säkerställer att endast en tråd åt gången går in i den kritiska sektionen; och kanaler tillåter att meddelanden skickas mellan trådar utan att direkt dela minne.
Varför lära sig Rust: Minnessäkerhet och orädd samtidighet
Rost har fått sitt rykte för att det erbjuder tre mycket värdefulla pelare för modern programmeringPrestanda, säkerhet och aktuella verktyg. Låt oss se varför dessa punkter är så relevanta.
Angående prestanda, rost kompilerar direkt till inbyggda binärfiler utan behov av en virtuell maskin eller tolk. Nollkostnadsabstraktionsmodellen syftar till att säkerställa att abstraktioner på hög nivå inte lägger till overhead vid körning. Därför är den idealisk för systemutveckling. spel, webbläsarkomponenter eller mikrotjänster med låg latens.
Minnessäkerhet baseras på dess ägande- och lånesystemDet finns ingen skräpinsamlare, men kompilatorn vet exakt vem som äger varje resurs, när den inte längre behövs och när den kan släppas. Detta förhindrar läckor, dinglande pekare och många av de fel som traditionellt har gjort C- och C++-programmering så farlig.
Inom tävlingsområdet bedriver Rust det som vanligtvis kallas "Samtidighet utan rädsla"Själva typsystemet förhindrar att datarötter finns i säker kod. Om du vill dela föränderlig data mellan trådar måste du använda lämpliga primitiv som Mutex, RwLock o Arc, och kompilatorn kommer att säkerställa att reglerna för aliasing och mutabilitet respekteras.
Utvecklingsupplevelsen förbättras med moderna verktyg som LaddningDen har en integrerad pakethanterare och bygginfrastruktur, och ett brett ekosystem av bibliotek (crates) som täcker allt från asynkrona nätverk (Tokyo) till webbramverk (Actix, Rocket, Axum). Allt detta stöds av en öppen, produktiv och ganska tålmodig community, särskilt för nybörjare.
Installation och viktiga verktyg: rustup, rustc och Cargo
För att skriva och köra dina första program i Rust är det vanliga sättet att börja genom att installera den officiella verktygskedjan med hjälp av rosta upp (se Fullständig introduktion till rost), ett enkelt installationsprogram och versionshanterare som fungerar på alla större operativsystem.
med rosta upp Du kan installera, uppdatera och växla mellan olika versioner av Rust (stabil, beta, nattlig) utan att något går sönder. Gå bara till den officiella Rust-verktygssidan och följ stegen för ditt system. När kompilatorn är installerad kommer den att vara tillgänglig. rustc, projektledaren cargo och hans egna rustup i din terminala.
kompilatorn rustc Det är det som omvandlar din källkod till körbara binärfiler eller bibliotek. Även om du skulle kunna anropa den direkt med kommandon som rustc main.rsI praktiken kommer du nästan alltid att arbeta via Cargo, som hanterar samtalen till rustc med rätt alternativ.
Det centrala verktyget i arbetsflödet är LaddningMed bara några få kommandon kan du skapa nya projekt, hantera beroenden, kompilera, köra, testa och publicera paket på crates.io. Några vanliga grundläggande kommandon är: cargo new, cargo build, cargo run, cargo test y cargo check, som kontrollerar koden utan att producera den slutliga körbara filen, perfekt för att snabbt upptäcka fel.
Om du vill experimentera utan att installera något, Rost lekplats (den officiella online-exekutorn) och plattformar som Replit låter dig skriva och köra små kodbitar från webbläsaren, perfekt för att experimentera med minne och samtidighetsexempel utan att behöva konfigurera hela miljön.
Ditt första program: Hej, Rust och grundläggande flöde
Det klassiska sättet att starta en konversation på vilket språk som helst är med det berömda "Hej världen". I Rust, en fil main.rs åtminstone skulle kunna innehålla något så enkelt som en funktion main som skriver ut en sträng på skärmen.
Nyckelordet fn indikerar att vi definierar en funktion, och main Detta är programmets startpunkt. Funktionens kodblock placeras inom klammerparenteserna. För att skriva till konsolen, använd makro println!, som accepterar en strängliteral (eller en mall med bokmärken) och skickar den till standardutdata som slutar med ett nyradstecken.
Om du kompilerar direkt med rustc main.rs, får du en körbar binärfil (till exempel main o main.exe (beroende på systemet). När du kör det ser du meddelandet i terminalen. Men det idiomatiska sättet att arbeta med Rust är att låta Cargo ta ledningen i projektet.
med cargo new nombre_proyecto En mappstruktur skapas automatiskt med en src/main.rs redan förberedd med ett "Hej världen" och en fil Cargo.toml som innehåller metadata och framtida beroenden. Därifrån, cargo run kompilera och köra binärfilenoch den kompilerar bara om när den upptäcker ändringar.
Det här arbetssättet är inte bara bekvämt, utan det får dig också att vänja dig vid att använda Rusts vanliga ekosystem från början, vilket är mycket användbart när du börjar lägga till lådor för samtidighet, nätverk, testning eller vad du än behöver.
// Vi deklarerar huvudfunktionen: programstartpunkt fn main() { // Vi använder makrot println! för att skriva ut text till konsolen println!("Hej världen!"); }
Variabler, mutabilitet och grundläggande datatyper
I Rust deklareras variabler med nyckelordet let, och som standard är oföränderligaMed andra ord, när du väl tilldelat dem ett värde kan du inte ändra det om du inte uttryckligen deklarerar det som föränderligt med mut.
Oföränderlighet som standard hjälper till att undvika subtila logiska fel, särskilt i samtidiga program där flera trådar kan vilja ändra samma värde. Om du behöver ändra det skriver du något i stil med let mut contador = 0;Därifrån kan du tilldela nya värden till contador.
Rost tillåter även den s.k. skuggningDu kan deklarera en ny variabel med samma namn inom samma scope och dölja den föregående. Detta är inte samma sak som att mutera, eftersom du skapar ett nytt värde (som till och med kan vara av en annan typ). Du kan till exempel konvertera från en sträng till ett heltal med samma namn, så länge det är en ny deklaration med let.
Rusts typsystem är statiskt, vilket betyder att Typen av varje variabel är känd vid kompileringTypinferens är dock ganska kraftfull: om du skriver let x = 5;Kompilatorn antar att det är en i32 Om du inte anger något annat. Du kan lägga till anteckningar som let x: i64 = 5; när du vill vara tydlig.
Bland de tillgängliga skalärtyperna finns teckenförsedda och oteckenförsedda heltal (i8, u8, i32, etc.), de flytande (f32, f64), booleska värden (bool) och Unicode-tecken (char). Dessa enkla typer är vanligtvis billiga att kopiera och många implementerar egenskapen Copyvilket innebär att när du tilldelar dem eller skickar dem till en funktion, kopieras de istället för att flyttas.
Strängar i Rust: &str och String
Texthantering i Rust kan vara lite förvirrande till en början eftersom det tydligt skiljer mellan kedje"skivor" och proprietära kedjorDe två viktigaste delarna är &str y String.
Un &str är en en bit av en oföränderlig kedjaEn vy av en UTF-8-bytesekvens som lagras någonstans. Typiska exempel inkluderar literaler som "Hola"som är av typen &'static str (De existerar under hela programmets livslängd och är inbäddade i binärfilen.) Skivor äger inte data; de pekar bara på den.
String, å andra sidan, är en egen sträng, muterbar och värd i heapenDen kan ändra storlek, sammanfogas, skickas mellan funktioner genom att flytta dess egenskap, etc. Den används ofta när du vill skapa dynamisk text eller lagra den långsiktigt inom strukturer.
I många scenarier kommer du att växla mellan det ena och det andra: till exempel kommer du att skapa en String::from("hola") från en skivaeller så lånar du en &str en String genom att skicka referenser till funktioner som bara behöver läsa.
Denna åtskillnad mellan ägd och lånad data är nyckeln till minneshantering och sträcker sig till resten av språket: samlingar, strukturer och enums följer samma idéer om vem som äger och vem som bara tittar.
Funktioner, kontrollflöde och kommentarer
Funktioner i Rust definieras med fn och tillåta att programmet organiseras i återanvändbara logiska enheter. Varje funktion specificerar typen av dess parametrar och dess returtyp efter en pil ->Om den inte returnerar något meningsfullt antas den unitära typen. ().
En viktig detalj är att det sista uttrycket i en funktion (eller i ett block) utan semikolon tas som det implicita returvärdet. Du kan använda return för tidiga returerMen i idiomatisk kod lämnar man ofta helt enkelt det slutliga uttrycket utan. ;.
Kontrollflödet hanteras med klassikerna if/elseloopar loop, while y forI Rost, if Det är ett uttryck som returnerar ett värdeså att du kan använda den direkt i en letförutsatt att grenarna returnerar samma typ. Loopar for De itererar vanligtvis över intervall eller samlingsiteratorer och är det rekommenderade alternativet istället för manuella index.
För att dokumentera koden och göra livet enklare för den som kommer efter (inklusive dig själv om en månad) kan du använda radkommentarer med // eller blockera med /* ... */Dessutom erbjuder Rust dokumentationskommentarer med /// som blir genererade dokument, även om det passar bättre in i stora projekt.
Ägande, utlåning och livslängd: grunden för minnessäkerhet
Här kommer vi till kärnan i Rusts minnesmodell: systemet med ägande, lån och livslängdDessa regler säkerställer att referenser alltid är giltiga och att minnet frigörs säkert utan ackumulerat skräp.
De grundläggande ägarreglerna är enkla att formulera, även om de kan vara svåra att internalisera till en början: Varje värde har en enda ägare.Det kan bara finnas en ägare åt gången; och när ägaren lämnar sitt område förstörs värdet och dess minne frigörs. Detta gäller till exempel en String: när blocket där det deklarerades är slutfört, anropas det automatiskt drop vilket frigör heap-minnet.
När du tilldelar ett korrekt värde till en annan variabel eller skickar det som värde till en funktion, flyttas egenskapen. Det betyder att den ursprungliga variabeln upphör att vara giltig efter flyttenDenna rörelsesemantik undviker dubbla utgåvor, eftersom det aldrig finns två ägare som försöker släppa samma resurs.
För att flera delar av programmet ska kunna komma åt samma värde utan att ägarskap ändras introducerar Rust referenser och lån. När du lånar skapar du en referens. &T (oföränderlig) eller &mut T (föränderlig) till värdet utan att överföra äganderätten. Lånet är begränsat av låneverifierarens regler., som kontrollerar att referenser inte överlever den data de pekar på och att muterbara och delade åtkomster inte blandas på ett farligt sätt.
Reglerna för lånet kan sammanfattas enligt följande: när som helst kan du antingen ha flera oföränderliga referenser till ett värde, eller en enda föränderlig referensMen inte båda samtidigt. Detta eliminerar kappvillkor i delat minne: antingen finns det många läsare, eller så finns det en isolerad skrivare; aldrig samtidiga läsare och skrivare på samma data vid samma tidpunkt.
Sammansatta typer: strukturer, enumer och smarta pekare
Rust erbjuder flera sätt att gruppera relaterad data i rikare strukturer, med början med strukturerEn struktur låter dig definiera en anpassad typ med namngivna fält, till exempel en användare med e-postadress, namn, aktivitetsstatus och inloggningsräknare.
För att skapa en instans av en struktur fyller du i alla dess fält, och du kan markera variabeln som innehåller den som muterbar för att ändra dess värden senare. Det finns också syntaxen för strukturuppdatering, som låter dig bygga en ny instans genom att återanvända vissa fält från en befintlig. ..otro_struct.
mycket uppräkningar De är ytterligare en viktig pelare: de låter dig definiera en typ som kan vara en av flera möjliga varianter, var och en med sina egna associerade data eller utan dem. Ett klassiskt exempel är en enum för IP-adresser, med en variant. V4 som lagrar fyra oktetter och en annan V6 som lagrar en sträng med IPv6-notation.
Rusts standardbibliotek innehåller två mycket viktiga enums: Option<T> y Result<T, E>Den första representerar närvaron eller frånvaron av ett värde (något eller ingenting) och används för att undvika nullpekare; den andra modellerar operationer som kan returnera ett korrekt resultat eller ett fel, vilket kräver att felhanteringen är explicit och säker.
För att hantera dynamiskt minne och dela data har Rust smarta pekare som Box<T>, vilket flyttar ett värde till heapen och bibehåller unikt ägande; Rc<T>, ett delat referensantal för miljöer med en enda tråd; och Arc<T>, liknar Rc men säkert för flera trådar. Att använda dem korrekt är avgörande när man kombinerar dynamiskt minne med samtidighet.
Last och lådornas ekosystem
Last är limmet som håller ihop Rust-ekosystemet: hanterar kompilering, beroenden och projektlivscykelnVarje projekt har en fil Cargo.toml som fungerar som ett manifest och deklarerar namn, version, språkutgåva och externa beroenden.
avsnitt Den här filen låter dig lista tredjepartslådor med deras versioner. När du kör cargo build o cargo runCargo laddar automatiskt ner dessa lådor från crates.io, kompilerar dem och länkar dem till ditt projekt. Det är så enkelt att lägga till till exempel slumptalsgeneratorer, webbramverk eller kryptografiska bibliotek.
Bland de vanligaste kommandona är cargo new att starta binära projekt o cargo new --lib för bibliotek; cargo build att kompilera i felsökningsläge; cargo build --release för att erhålla en optimerad, produktionsorienterad version; och cargo test för att köra batteriet av tester.
cargo check Den förtjänar ett särskilt omnämnande: den kompilerar koden till en mellanliggande punkt utan att generera en binärfil, vilket gör den vara mycket snabb på att upptäcka kompileringsfelDet är perfekt för att iterera snabbt medan lånekontrollen påpekar problem med egenskaper, referenser och livslängder.
Tack vare detta ekosystem är det vanligt att strukturera sina projekt som små, väldefinierade lådor, dela kod mellan dem och återanvända lösningar som skapats av communityn. För avancerad samtidighet har man till exempel lådor som Tokio för asynkron programmering eller crossbeam för högpresterande samtidiga datastrukturer.
Samtidighet i Rust: trådar, mutexer, kanaler och atomer
Samtidighet är en av anledningarna till att Rust genererar så mycket intresse: det låter dig dra nytta av flerkärniga processorer. utan att hamna i de typiska felen med trådar och delat minneOm det här är första gången du tar upp dessa ämnen är det bra att skilja mellan flera olika begrepp.
Samtidighet innebär att flera uppgifter körs som överlappar varandra i tid, antingen på en eller flera kärnor. I Rust kan du skapa systemtrådar för att utföra arbete parallellt, och språket vägleder dig för att säkerställa att datadelning mellan dem är säker. Ett klassiskt fel är kappvillkoret, där två trådar kommer åt och ändrar data samtidigt, och resultatet beror på körningsordningen – något som är mycket svårt att felsöka.
För att koordinera åtkomst till delad data förlitar sig Rust på primitiva funktioner som mutexvilket garanterar ömsesidig uteslutning: endast en tråd kan komma in i den kritiska sektionen åt gången. I kombination med Arc<T> För att dela äganderätt mellan trådar är det möjligt att bygga delade datastrukturer som följer reglerna för ägande och lån.
En annan vanlig form av intertrådad kommunikation, starkt uppmuntrad i Rust, är meddelandeöverföring med hjälp av kanalerEn kanal har en sändande ände och en mottagande ände; trådar skickar meddelanden (värden) genom den, vilket minskar användningen av föränderligt delat minne och förenklar resonemanget om systemets tillstånd.
När man fördjupar sig i samtidighet på låg nivå visas följande: atomtyperAtomvariabler nås genom operationer som är odelbara ur ett trådperspektiv. Detta möjliggör implementering av delade räknare, tillståndsflaggor, låsfria köer och mer. Att behärska atomvariabler kräver förståelse för minnesmodeller och åtkomstkommandon, så många utvecklare föredrar att börja med mutexer och kanaler innan de fördjupar sig i dessa detaljer.
Första stegen och resurser för att lära sig samtidighet och atomär
Om du går in på arenan utan tidigare erfarenhet är det klokaste att göra bygga en solid grund av allmänna koncept innan man tar sig an avancerade verktyg som Rusts atomtyper. Böcker som "Programming Rust" erbjuder en gradvis introduktion, men det är normalt att verk som fokuserar på atomtyper och lås verkar komplicerade till en början.
För att underlätta är det lämpligt att först bekanta sig med Traditionella trådar, ömsesidig uteslutning och meddelandeöverföring i Rust. Lek med exempel på std::thread, std::sync::Mutex, std::sync::Arc och kanaler av std::sync::mpsc Det hjälper dig att förstå hur kompilatorn vägleder dig och vilka fel den undviker.
Parallellt rekommenderas det starkt att granska introduktionsresurser om samtidighet i allmänhet, även om de inte fokuserar på Rust: att förstå vad kappvillkor är, vad blockering innebär, vad delat minne innebär kontra meddelandeöverföring, och hur lås används. När dessa begrepp blir naturliga för dig upphör atomfysiken att vara "svart magi". och de blir bara ytterligare ett verktyg, bara ett mycket ömtåligt sådant.
När du återgår till mer avancerade texter om atomer och lås i Rust, blir det mycket lättare att följa resonemanget om du redan förstår vilket problem varje konstruktion försöker lösa: från en enkel trådsäker räknare till låsfria strukturer som minimerar konkurrens.
I slutändan erbjuder Rust både högnivåprimitiver och verktyg på mycket låg nivå, och nyckeln är att alltid välja den säkraste abstraktionsnivån som löser ditt problem, genom att tillgripa atomkod. unsafe bara när det verkligen tillför värde och du fullt ut förstår dess konsekvenser.
Hela detta ekosystem av typer, ägande, lån, lådor, verktyg och samtidighetsprimitiver kombineras för att erbjuda ett språk att skriva i. snabb, robust och underhållbar programvaraDetta minimerar många typer av fel som historiskt sett har plågat systemprogrammering. När du övar med små projekt, övningar som Rustlings och officiell dokumentation, kommer dessa koncept att gå från att verka som strikta regler till att bli en allierad som varnar dig innan problemet når produktionsfasen.
Passionerad författare om bytesvärlden och tekniken i allmänhet. Jag älskar att dela med mig av min kunskap genom att skriva, och det är vad jag kommer att göra i den här bloggen, visa dig alla de mest intressanta sakerna om prylar, mjukvara, hårdvara, tekniska trender och mer. Mitt mål är att hjälpa dig att navigera i den digitala världen på ett enkelt och underhållande sätt.