Rust-opplæring: Minnesikkerhet og samtidighet

Siste oppdatering: 04/12/2025
Forfatter: Isaac
  • Rust sikrer minnesikkerhet ved kompilering gjennom eierskap, lån og levetid, uten å bruke søppelinnsamling.
  • Typesystemet og aliasing-reglene tillater samtidighet uten dataløp ved bruk av mutexer, kanaler og smarte pekere.
  • Cargo, crates.io og et aktivt økosystem forenkler avhengighetshåndtering, kompilering, testing og distribusjon.
  • Å forstå strukturer, enums, alternativer og resultater er nøkkelen til å håndtere feil og modellere sikre data i samtidige applikasjoner.

Programmeringsspråk C vs. Rust: fordeler og ulemper

Rust har blitt et av de språkene som Enhver systemutvikler ender opp med å høre det om og om igjen.Den er like rask som C og C++, men med et nesten obsessivt fokus på minnesikkerhet og godt utført samtidighet. Dette er ikke bare tom markedsføring: designet dreier seg om at kompilatoren oppdager feil under kompilering – feil som du i andre språk bare ser når systemet allerede er i produksjon ... eller når det krasjer.

Hvis du er interessert i å forstå Hvordan Rust oppnår sikkert minne uten søppelinnsamling og samtidighet uten frykt for datakjøringerDenne veiledningen er for deg. Vi vil dekke alt fra det grunnleggende i språket og dets økosystem til viktige konsepter som eierskap, lån, sammensatte typer, verktøy som Cargo, og til og med ta en titt på atomtyper og låsing fra et mer tilgjengelig perspektiv for de som er nye innen samtidighet, alt med fokus på sikkerhet og ytelse.

Rust-opplæring: Ytelse, minnesikkerhet og samtidighet

Rust er et programmeringsspråk programmering generell bruk og multiparadigme, designet for lavnivå systemprogrammering så vel som for prosjekter på høyt nivåFra OSFra spillmotorer og nettlesere til høytytende webtjenester, oppsto det i Mozilla med mål om å forbedre programvaresikkerheten, spesielt i sensitive komponenter som en nettlesermotor.

Dens definerende kjennetegn er at garanterer minnesikkerhet ved kompileringstid uten å bruke en søppelinnsamler. I stedet bruker Rust et eierskapssystem og en lånekontrollør som sporer levetiden til hver verdi og dens referanser. Dette unngår klassiske problemer som dinglende pekere, bufferoverløp eller minnelekkasjer uten å kreve automatisk referansetelling eller søppelinnsamling.

Videre er Rust designet for å gjøre det enklere trygt oppmøteType- og eierskapsmodellen forhindrer datakappløp mellom tråder, i hvert fall mens den forblir i sikker Rust-kode. Dette betyr at mange farlige situasjoner oppdages ved kompilering, før en enkelt linje kjøres.

Av alle disse grunnene, store selskaper som Dropbox, Microsoft, Amazon eller Google De har tatt i bruk Rust i kritiske deler av infrastrukturen sin. Og det er ingen tilfeldighet at det har toppet Stack Overflow-avstemninger i årevis som et av de "mest elskede" språkene blant utviklere: det kombinerer ytelse i C++-stil med et moderne verktøysett (Cargo, crates.io) og et veldig aktivt fellesskap, de såkalte Rustaceans.

Grunnleggende konsepter: programmeringsspråk, typer og minne

Før vi går inn på detaljene rundt minnesikkerhet og samtidighet, er det verdt å avklare noen generelle konsepter som dukker opp gjennomgående. tiden Når man jobber med rust, spesielt hvis du kommer fra andre språk eller akkurat har begynt å programmere.

Et programmeringsspråk er til syvende og sist, et sett med regler og strukturer som lar deg beskrive algoritmer og transformere dem til kjørbare programmer. Rust kompilerer til innebygd maskinkode ved hjelp av kompilatoren. rustcDerfor er ytelsen du får vanligvis på nivå med C og C++.

Minnehåndtering er prosessen der et program reserverer og frigjør minneblokker under kjøringFeil på dette området er ofte fatale: minnelekkasjer (manglende frigjøring av ubrukt minne), datakorrupsjon fra skriving utenfor grensene, eller bruk av minne etter at det har blitt frigjort. Rust adresserer dette med et veldig sterkt typesystem og formelle regler for eierskap, lån og levetid.

Rust inneholder også begreper som smarte typer og pekereEn type beskriver hva slags data en variabel lagrer (heltall, flyttall, strenger, strukturer osv.) og hvordan de kan manipuleres. Smarte pekere (for eksempel Box, Rc y Arc) er strukturer som innkapsler minneadresser og legger til ekstra logikk for å administrere ressurser på en sikker måte, for eksempel å telle delte referanser eller flytte verdier til heapen.

Innen konkurransefeltet brukes konsepter som kappløpsbetingelser, mutexer og kanaler De blir uunnværlige: en kappløpstilstand oppstår når flere tråder får tilgang til og endrer en delt ressurs samtidig uten skikkelig koordinering; en mutex (gjensidig utelukkelse) sikrer at bare én tråd går inn i den kritiske delen om gangen; og kanaler tillater at meldinger sendes mellom tråder uten å dele minne direkte.

Hvorfor lære Rust: Minnesikkerhet og fryktløs samtidighet

Rust har fått berømmelsen sin fordi den tilbyr tre svært verdifulle søyler for moderne programmeringYtelse, sikkerhet og nåværende verktøy. La oss se hvorfor disse punktene er så relevante.

Når det gjelder ytelse, Rust kompilerer direkte til native binærfiler uten behov for en virtuell maskin eller tolk. Nullkostnadsabstraksjonsmodellen har som mål å sikre at abstraksjoner på høyt nivå ikke legger til overhead under kjøretid. Derfor er den ideell for systemutvikling. spillet, nettleserkomponenter eller mikrotjenester med lav forsinkelse.

Minnesikkerhet er basert på dens eierskaps- og lånesystemDet finnes ingen søppelinnsamler, men kompilatoren vet nøyaktig hvem som eier hver ressurs, når den ikke lenger er nødvendig, og når den kan frigis. Dette forhindrer lekkasjer, dinglende pekere og mange av feilene som tradisjonelt har gjort C- og C++-programmering så farlig.

Innen konkurranseområdet driver Rust med det som vanligvis kalles «Samtidighet uten frykt»Selve typesystemet forhindrer at datarøtter finnes i sikker kode. Hvis du vil dele endringsbare data mellom tråder, må du bruke passende primitiver som Mutex, RwLock o Arc, og kompilatoren vil sørge for at reglene for aliasing og mutabilitet respekteres.

  Slik desinfiserer du Windows med Malwarebytes trinn for trinn

Utviklingsopplevelsen forbedres med moderne verktøy som CargoDen har en integrert pakkebehandler og byggeinfrastruktur, og et bredt økosystem av biblioteker (crates) som dekker alt fra asynkrone nettverk (Tokyo) til webrammeverk (Actix, Rocket, Axum). Alt dette støttes av et åpent, produktivt og ganske tålmodig fellesskap, spesielt for nybegynnere.

Installasjon og viktige verktøy: rustup, rustc og Cargo

For å skrive og kjøre dine første programmer i Rust, er den vanlige måten å starte på å installere den offisielle verktøykjeden ved hjelp av ruste opp (se Fullstendig introduksjon til Rust), et enkelt installasjonsprogram og versjonsbehandler som fungerer på alle større operativsystemer.

med ruste opp Du kan installere, oppdatere og bytte mellom forskjellige versjoner av Rust (stabil, beta, nattlig) uten å ødelegge noe. Bare gå til den offisielle Rust-verktøysiden og følg trinnene for systemet ditt. Når kompilatoren er installert, vil den være tilgjengelig. rustc, prosjektlederen cargo og hans egne rustup i din terminal.

kompilatoren rustc Det er det som transformerer kildekoden din til kjørbare binærfiler eller biblioteker. Selv om du kan kalle den direkte med kommandoer som rustc main.rsI praksis vil du nesten alltid jobbe gjennom Cargo, som håndterer samtalene til rustc med de riktige alternativene.

Det sentrale verktøyet i arbeidsflyten er CargoMed bare noen få kommandoer kan du opprette nye prosjekter, administrere avhengigheter, kompilere, kjøre, teste og publisere pakker på crates.io. Noen vanlige grunnleggende kommandoer er: cargo new, cargo build, cargo run, cargo test y cargo check, som sjekker koden uten å produsere den endelige kjørbare filen, ideelt for å oppdage feil raskt.

Hvis du vil fikle uten å installere noe, Rust Lekeplass (den offisielle online-eksekuteren) og plattformer som Replit lar deg skrive og kjøre små kodebiter fra nettleseren, perfekt for å eksperimentere med minne- og samtidighetseksempler uten å måtte sette opp hele miljøet.

Ditt første program: Hallo, Rust og grunnleggende flyt

Den klassiske måten å starte en samtale på, uansett språk, er med det berømte «Hallo verden». I Rust er en fil main.rs kunne i det minste inneholde noe så enkelt som en funksjon main som skriver ut en streng på skjermen.

Stikkordet fn indikerer at vi definerer en funksjon, og main Dette er programmets inngangspunkt. Funksjonens kodeblokk går innenfor klammeparentesene. For å skrive til konsollen, bruk makro println!, som aksepterer en strenglitteral (eller en mal med bokmerker) og sender den til standardutdata som slutter med et linjeskifttegn.

Hvis du kompilerer direkte med rustc main.rs, vil du få en kjørbar binærfil (for eksempel, main o main.exe (avhengig av systemet). Når du kjører det, vil du se meldingen i terminalen. Men den idiomatiske måten å jobbe med Rust på er å la Cargo ta ledelsen i prosjektet.

med cargo new nombre_proyecto En mappestruktur opprettes automatisk med en src/main.rs allerede forberedt med et «Hallo verden» og en fil Cargo.toml som inneholder metadata og fremtidige avhengigheter. Derfra, cargo run kompiler og kjør binærfilenog den kompilerer bare på nytt når den oppdager endringer.

Denne arbeidsmåten er ikke bare praktisk, men den hjelper deg også med å bruke standard Rust-økosystemet fra starten av, noe som er veldig nyttig når du begynner å legge til kasser for samtidighet, nettverk, testing eller hva du enn trenger.

// Vi deklarerer hovedfunksjonen: programstartpunkt fn main() { // Vi bruker println!-makroen til å skrive ut tekst til konsollen println!("Hallo verden!"); }

Variabler, mutabilitet og grunnleggende datatyper

I Rust deklareres variabler med nøkkelordet let, og som standard er uforanderligeMed andre ord, når du har tilordnet dem en verdi, kan du ikke endre den med mindre du eksplisitt erklærer den som foranderlig med mut.

Uforanderlighet som standard bidrar til å unngå subtile logiske feil, spesielt i samtidige programmer der flere tråder kanskje vil endre den samme verdien. Hvis du trenger å endre den, skriver du noe sånt som let mut contador = 0;Derfra kan du tilordne nye verdier til contador.

Rust tillater også den såkalte skyggingDu kan deklarere en ny variabel med samme navn innenfor samme omfang, og skjule den forrige. Dette er ikke det samme som å mutere, fordi du oppretter en ny verdi (som til og med kan være av en annen type). For eksempel kan du konvertere fra en streng til et heltall med samme navn, så lenge det er en ny deklarasjon med let.

Rusts typesystem er statisk, noe som betyr at Typen til hver variabel er kjent ved kompileringTypeinferens er imidlertid ganske kraftig: hvis du skriver let x = 5;Kompilatoren antar at det er en i32 Med mindre du oppgir noe annet. Du kan legge til notater som let x: i64 = 5; når du vil være tydelig.

Blant de tilgjengelige skalartypene er fortegns- og ufortegns-heltallene (i8, u8, i32osv.), de flytende (f32, f64), de boolske verdiene (bool) og Unicode-tegn (char). Disse enkle typene er vanligvis billige å kopiere, og mange implementerer egenskapen Copysom betyr at når du tilordner dem eller sender dem til en funksjon, kopieres de i stedet for flyttes.

Strenger i Rust: &str og String

Teksthåndtering i Rust kan være litt forvirrende i starten fordi det tydelig skiller mellom kjede-"skiver" og proprietære kjederDe to nøkkeldelene er &str y String.

Un &str er en stykke av uforanderlig kjedeEn visning av en UTF-8 bytesekvens lagret et sted. Typiske eksempler inkluderer literaler som "Hola"som er av typen &'static str (De eksisterer i hele programmets levetid og er innebygd i binærfilen.) Skiver eier ikke dataene; de ​​peker bare til dem.

  Slik tilpasser du oppgavelinjen grundig i Windows 11: Komplett guide og avanserte tips

String, derimot, er en egen streng, muterbar og lagret i heapenDen kan endres i størrelse, sammenkobles, overføres mellom funksjoner ved å flytte egenskapen, osv. Den brukes ofte når du vil bygge dynamisk tekst eller lagre den langsiktig i strukturer.

I mange scenarier vil du veksle mellom det ene og det andre: for eksempel vil du opprette en String::from("hola") fra et stykkeeller du låner en &str en String ved å sende referanser til funksjoner som bare trenger å lese.

Denne separasjonen mellom eide og lånte data er nøkkelen til minnehåndtering og strekker seg til resten av språket: samlinger, strukturer og enums følger de samme ideene om hvem som eier og hvem som bare ser.

Funksjoner, kontrollflyt og kommentarer

Funksjoner i Rust er definert med fn og tillate at programmet organiseres i gjenbrukbare logiske enheter. Hver funksjon spesifiserer typen av parameterne og returtypen etter en pil ->Hvis den ikke returnerer noe meningsfullt, antas den unitære typen. ().

En viktig detalj er at det siste uttrykket i en funksjon (eller en hvilken som helst blokk) uten semikolon tas som den implisitte returverdien. Du kan bruke return for tidlig returMen i idiomatisk kode lar du ofte ganske enkelt det endelige uttrykket stå uten. ;.

Kontrollflyten håndteres med klassikerne if/elseløkker loop, while y forI Rust, if Det er et uttrykk som returnerer en verdislik at du kan bruke den direkte i en letforutsatt at grenene returnerer samme type. Looper for De itererer vanligvis over områder eller samlingsiteratorer, og er det anbefalte alternativet i stedet for manuelle indekser.

For å dokumentere koden og gjøre livet enklere for de som kommer etterpå (inkludert deg selv om en måned), kan du bruke linjekommentarer med // eller blokkere med /* ... */I tillegg tilbyr Rust kommentarer til dokumentasjonen med /// som blir genererte dokumenter, selv om det passer bedre inn i store prosjekter.

Eierskap, utlån og levetid: grunnlaget for minnesikkerhet

Her kommer vi til kjernen av Rusts minnemodell: systemet med eierskap, låneopptak og levetidDisse reglene sikrer at referansene alltid er gyldige og at minnet frigjøres trygt uten akkumulert søppel.

De grunnleggende eierreglene er enkle å formulere, selv om de kan være vanskelige å internalisere i starten: Hver verdi har én eier.Det kan bare være én eier om gangen; og når eieren forlater sitt virkeområde, blir verdien ødelagt og minnet frigjort. Dette gjelder for eksempel en String: når blokken der den ble deklarert er fullført, blir den automatisk påkalt drop som frigjør heap-minnet.

Når du tilordner en riktig verdi til en annen variabel eller sender den som verdi til en funksjon, flyttes egenskapen. Dette betyr at den opprinnelige variabelen slutter å være gyldig etter flyttingenDenne bevegelsessemantikken unngår doble utgivelser, fordi det aldri er to eiere som prøver å utgi den samme ressursen.

For å la flere deler av programmet få tilgang til den samme verdien uten å endre eierskap, introduserer Rust referanser og lån. Når du låner, oppretter du en referanse. &T (uforanderlig) eller &mut T (endringsbar) til verdien uten å overføre eierskap. Lånet er begrenset av låneverifisererens regler., som kontrollerer at referansene ikke overlever dataene de peker til, og at endringsbare og delte tilganger ikke blandes farlig.

Reglene for lånet kan oppsummeres som følger: til enhver tid kan du enten ha flere uforanderlige referanser til en verdi, eller en enkelt foranderlig referanseMen ikke begge deler samtidig. Dette eliminerer kappløpsbetingelser i delt minne: enten er det mange lesere, eller så er det en isolert skriver; aldri samtidige lesere og skrivere på de samme dataene i samme øyeblikk.

Sammensatte typer: strukturer, enums og smarte pekere

Rust tilbyr flere måter å gruppere relaterte data i rikere strukturer, og starter med strukturerEn struktur lar deg definere en tilpasset type med navngitte felt, for eksempel en bruker med e-post, navn, aktivitetsstatus og påloggingsteller.

For å opprette en instans av en struktur, fyller du ut alle feltene, og du kan markere variabelen som inneholder den som endringsbar for å endre verdiene senere. Det finnes også en strukturoppdateringssyntaks, som lar deg bygge en ny instans ved å gjenbruke noen felt fra en eksisterende. ..otro_struct.

den opplistinger De er en annen viktig søyle: de lar deg definere en type som kan være én av flere mulige varianter, hver med sine egne tilknyttede data eller uten dem. Et klassisk eksempel er en enum for IP-adresser, med én variant. V4 som lagrer fire oktetter og en annen V6 som lagrer en streng med IPv6-notasjon.

Rusts standardbibliotek inneholder to svært viktige enums: Option<T> y Result<T, E>Den første representerer tilstedeværelsen eller fraværet av en verdi (noe eller ingenting), og brukes til å unngå nullpekere; den andre modellerer operasjoner som kan returnere et riktig resultat eller en feil, som krever at feilhåndteringen er eksplisitt og sikker.

For å administrere dynamisk minne og dele data har Rust smarte pekere som Box<T>, som flytter en verdi til heapen og opprettholder unikt eierskap; Rc<T>, et delt referanseantall for enkelttrådede miljøer; og Arc<T>, lik Rc men trygt for flere tråder. Riktig bruk av dem er avgjørende når man kombinerer dynamisk minne med samtidighet.

Last og kasseøkosystemet

Last er limet som holder Rust-økosystemet sammen: administrerer kompilering, avhengigheter og prosjektlivssyklusHvert prosjekt har en fil Cargo.toml som fungerer som et manifest, og deklarerer navn, versjon, språkutgave og eksterne avhengigheter.

  Rettet: Windows 10 Intern PCI Bus Driver Feil

seksjon Denne filen lar deg liste opp tredjepartskasser med versjonene deres. Når du kjører cargo build o cargo runCargo laster automatisk ned disse kassene fra crates.io, kompilerer dem og kobler dem til prosjektet ditt. Så enkelt er det å legge til for eksempel tilfeldige tallgeneratorer, webrammeverk eller kryptografiske biblioteker.

Blant de vanligste kommandoene er cargo new å starte binære prosjekter o cargo new --lib for biblioteker; cargo build å kompilere i feilsøkingsmodus; cargo build --release å oppnå en optimalisert, produksjonsorientert versjon; og cargo test å kjøre batteriet av tester.

cargo check Den fortjener spesiell omtale: den kompilerer koden til et mellomliggende punkt uten å generere en binærfil, noe som gjør den være veldig rask til å oppdage kompileringsfeilDen er perfekt for rask iterasjon mens lånekontrolløren påpeker problemer med egenskaper, referanser og levetider.

Takket være dette økosystemet er det vanlig å strukturere prosjektene dine som små, veldefinerte kasser, dele kode mellom dem og gjenbruke løsninger laget av fellesskapet. For avansert samtidighet, for eksempel, vil du ha kasser som Tokio for asynkron programmering eller crossbeam for høytytende samtidige datastrukturer.

Samtidighet i Rust: tråder, mutexer, kanaler og atomer

Samtidighet er en av grunnene til at Rust genererer så mye interesse: det lar deg dra nytte av flerkjerneprosessorer. uten å falle inn i de typiske feilene i tråder og delt minneHvis dette er første gang du tar for deg disse temaene, er det nyttig å skille mellom flere konsepter.

Samtidighet innebærer å utføre flere oppgaver som overlapper i tid, enten på én eller flere kjerner. I Rust kan du opprette systemtråder for å utføre arbeid parallelt, og språket veileder deg for å sikre at datadeling mellom dem er trygg. En klassisk feil er kappløpstilstanden, der to tråder får tilgang til og endrer data samtidig, og resultatet avhenger av utførelsesrekkefølgen – noe som er veldig vanskelig å feilsøke.

For å koordinere tilgang til delte data er Rust avhengig av primitiver som mutexsom garanterer gjensidig ekskludering: bare én tråd kan gå inn i den kritiske seksjonen om gangen. I kombinasjon med Arc<T> For å dele eierskap mellom tråder er det mulig å bygge delte datastrukturer som overholder reglene for eierskap og lån.

En annen vanlig form for interthreaded kommunikasjon, sterkt oppmuntret i Rust, er meldingsoverføring ved hjelp av kanalerEn kanal har en sendende ende og en mottakende ende; tråder sender meldinger (verdier) gjennom den, noe som reduserer bruken av foranderlig delt minne og forenkler resonnement om systemets tilstand.

Når du går dypere inn i lavnivåkonkurranse, vises følgende: atomtyperAtomvariabler nås gjennom operasjoner som er udelelige fra et trådperspektiv. Dette muliggjør implementering av delte tellere, tilstandsflagg, låsefrie køer og mer. Å mestre atomvariabler krever forståelse av minnemodeller og tilgangskommandoer, så mange utviklere foretrekker å starte med mutexer og kanaler før de går i dybden på disse detaljene.

Første steg og ressurser for å lære samtidighet og atomteknologi

Hvis du går inn på arenaen uten tidligere erfaring, er det klokeste å gjøre det bygge et solid fundament av generelle konsepter før man tar fatt på avanserte verktøy som Rusts atomtyper. Bøker som «Programming Rust» tilbyr en gradvis introduksjon, men det er normalt at verk som fokuserer på atomtyper og låser virker tette i starten.

For enkelhets skyld er det lurt å først gjøre seg kjent med Tradisjonelle tråder, gjensidig ekskludering og meldingsoverføring i Rust. Lek med eksempler på std::thread, std::sync::Mutex, std::sync::Arc og kanaler av std::sync::mpsc Det hjelper deg å forstå hvordan kompilatoren veileder deg og hvilke feil den unngår.

Parallelt anbefales det på det sterkeste å gjennomgå innledende ressurser om samtidighet generelt, selv om de ikke fokuserer på Rust: å forstå hva kappløpsbetingelser er, hva blokkering betyr, hva delt minne innebærer kontra meldingsoverføring, og hvordan låser brukes. Når disse konseptene blir naturlige for deg, slutter atomfysikk å være «svart magi». og de blir bare et annet verktøy, bare et veldig delikat et.

Når du går tilbake til mer avanserte tekster om atomer og låser i Rust, vil det være mye lettere å følge resonnementet hvis du allerede forstår hvilket problem hver konstruksjon prøver å løse: fra en enkel trådsikker teller til låsefrie strukturer som minimerer konflikt.

Til syvende og sist tilbyr Rust både primitiver på høyt nivå og verktøy på svært lavt nivå, og nøkkelen er å alltid velge det sikreste abstraksjonsnivået som løser problemet ditt, ved å ty til atomkode. unsafe bare når det virkelig tilfører verdi og du fullt ut forstår implikasjonene.

Hele dette økosystemet av typer, eierskap, lån, kasser, verktøy og samtidighetsprimitiver kombineres for å tilby et språk å skrive i. rask, robust og vedlikeholdbar programvareDette minimerer mange typer feil som historisk sett har plaget systemprogrammering. Etter hvert som du øver med små prosjekter, øvelser som Rustlings og offisiell dokumentasjon, vil disse konseptene gå fra å virke som strenge regler til å bli en alliert som advarer deg før problemet når produksjon.

Introduksjon til Rust-språket med eksempler-0
Relatert artikkel:
Komplett introduksjon til rust: En praktisk nybegynnerguide med eksempler