Rust programavimo kalba: saugumas ir greitis

Kodėl visi staiga kalba apie Rust?

Jei paskutinius kelerius metus sekate technologijų naujienas, tikriausiai pastebėjote keistą reiškinį – vis daugiau programuotojų ir įmonių pradeda kalbėti apie Rust programavimo kalbą su beveik religingu entuziazmu. Microsoft perkelia dalį Windows komponentų į Rust, Google naudoja ją Android projekte, o Mozilla (kuri ir sukūrė Rust) jau seniai perašė didelę dalį Firefox naršyklės. Net Linux branduolio kūrėjai, kurie dešimtmečiais tvirtai laikėsi C kalbos, dabar leidžia rašyti modulius Rust kalba.

Kas gi vyksta? Ar tai tik dar viena madinga technologija, kuri po metų-kitų išnyks kaip daugybė kitų? Atsakymas yra ne. Rust sprendžia tikras, fundamentalias problemas, su kuriomis programuotojai kovoja jau kelis dešimtmečius. Ir daro tai labai elegantiškai.

Tradiciškai programavimo kalbose teko rinktis: arba greitis ir kontrolė (C, C++), arba saugumas ir patogumas (Java, Python, Go). Rust teigia, kad galima turėti abu dalykus vienu metu. Ir, kas svarbiausia – tai ne tik marketingas.

Atminties valdymas be šiukšlių surinkėjo

Viena didžiausių Rust inovacijų yra jos požiūris į atminties valdymą. Daugelis modernių kalbų naudoja šiukšlių surinkėją (garbage collector) – sistemą, kuri automatiškai valo nebereikalingą atmintį. Tai patogu, bet turi kainą: neprognozuojamos pauzės, didesnis atminties naudojimas ir bendras našumo sumažėjimas.

C ir C++ leidžia programuotojui valdyti atmintį rankiniu būdu, kas suteikia maksimalų greitį, bet kartu atidaro duris daugybei klaidų. Memory leaks, use-after-free, double-free, dangling pointers – šie terminai yra košmaras kiekvienam sistemų programuotojui. Tyrimai rodo, kad apie 70% visų saugumo spragų Microsoft ir Google produktuose yra susijusios su atminties valdymo klaidomis.

Rust siūlo trečią kelią – ownership sistemą. Tai kompiliatoriaus lygmens mechanizmas, kuris kompiliavimo metu patikrina, ar jūsų kodas teisingai valdo atmintį. Jei ne – kodas tiesiog nekompiliuojasi. Skamba griežtai? Taip. Bet rezultatas yra toks: jei jūsų Rust programa sukompiliavosi, ji beveik garantuotai neturės atminties valdymo klaidų.

Praktiškai tai veikia per tris pagrindines taisykles: kiekviena reikšmė turi vieną savininką, savininkas gali būti tik vienas, kai savininkas išeina iš scope, reikšmė automatiškai sunaikinama. Papildomai yra borrowing sistema, kuri leidžia laikinai „pasiskolinti” nuorodą į duomenis be nuosavybės perdavimo.

Konkurencija be baimės

Daugiagijis programavimas tradiciškai yra viena sudėtingiausių programavimo sričių. Race conditions, deadlocks, data races – šios problemos kankina net patyrusius programuotojus. Klasikinis pavyzdys: du thread’ai bando vienu metu modifikuoti tą patį duomenų objektą. Rezultatas? Neprognozuojamas elgesys, kuris gali pasireikšti tik retais atvejais produkcijoje.

Rust ownership sistema čia vėl įsikiša. Kompiliatorius tiesiog neleis jums sukurti kodo, kuris galėtų turėti data race. Jei bandote dalintis duomenimis tarp thread’ų nesaugiai, gausite kompiliavimo klaidą su gana aiškiu paaiškinmu, kas negerai.

Tai nereiškia, kad Rust apsunkina konkurentų programavimą. Priešingai – kai išmokstate dirbti su ownership sistema, konkurentų kodą rašyti tampa lengviau, nes nebeturite nuolat galvoti „ar čia tikrai saugu?”. Kompiliatorius tampa jūsų partneriu, kuris padeda išvengti klaidų.

Praktinis pavyzdys: jei norite apdoroti didelį duomenų masyvą lygiagrečiai, galite naudoti rayon biblioteką. Ji leidžia paprastai konvertuoti sekvencinius iteratorius į lygiagrečius, ir kompiliatorius užtikrins, kad visa tai vyks saugiai. Kode tai atrodo maždaug taip:

items.par_iter().map(|item| process(item)).collect()

Viena eilutė, ir jūsų kodas dabar naudoja visus CPU branduolius. Be data races. Be undefined behavior.

Greitis, kuris konkuruoja su C

Kalbant apie našumą, Rust yra tikrai greita. Benchmark’ai rodo, kad Rust programos dažnai pasiekia C ir C++ greitį, kartais net pranoksta. Kaip tai įmanoma?

Pirma, Rust nekompromituoja našumo vardan patogumo. Nėra šiukšlių surinkėjo, nėra runtime overhead, nėra paslėptų alokacijų. Tai, ką matote kode, yra tai, ką gaunate mašininiame kode.

Antra, Rust kompiliatorius naudoja LLVM backend’ą – tą patį, kurį naudoja Clang (modernus C/C++ kompiliatorius). Tai reiškia, kad Rust gauna visas pažangiausias optimizacijas, kurias LLVM siūlo.

Trečia, Rust tipo sistema suteikia kompiliatoriui daugiau informacijos nei C ar C++. Pavyzdžiui, kompiliatorius žino, kad du &mut (mutable) nuorodos niekada nerodys į tą patį atminties regioną. Tai leidžia atlikti agresyvesnes optimizacijas be rizikos sugadinti programos logiką.

Žinoma, teorija yra viena, o praktika – kita. Realybėje greitis priklauso nuo to, kaip rašote kodą. Rust suteikia įrankius rašyti greitą kodą, bet neapsaugo nuo algoritmiškai neefektyvių sprendimų. Tačiau kai reikia maksimalaus našumo, Rust leidžia nusileisti į žemesnį lygį su unsafe blokeliais, kur galite daryti tuos pačius dalykus kaip C, bet su aiškiu žymėjimu, kad čia galimos problemos.

Klaidos, kurios neįvyksta runtime’e

Vienas iš Rust motyvų yra „jei kompiliuojasi, tai veikia”. Žinoma, tai šiek tiek perdėta, bet esmė teisinga. Rust tipo sistema ir kompiliatorius sugauna daugybę klaidų, kurios kitose kalbose pasireikštų tik vykdymo metu.

Pavyzdžiui, Option ir Result tipai verčia jus aiškiai apdoroti situacijas, kai reikšmės gali nebūti arba operacija gali nepavykti. Nėra null pointer’ių, nėra neapdorotų exception’ų. Jei funkcija gali nepavykti, jos grąžinimo tipas bus Result<T, E>, ir jūs privalote kaip nors su tuo susidoroti.

Tai gali atrodyti varginantis pradžioje, bet ilgalaikėje perspektyvoje sutaupo daugybę laiko. Kiek kartų jūs gavote production’e klaidą „NullPointerException” arba „undefined is not a function”? Rust tokių klaidų tiesiog neturi.

Pattern matching sistema daro klaidų apdorojimą ne tik saugų, bet ir patogų. Galite aiškiai matyti visus galimus scenarijus ir kaip su jais elgiatės. Kompiliatorius net įspės, jei pamiršote apdoroti kokį nors variantą.

Ekosistema ir įrankiai

Programavimo kalba be geros ekosistemos yra tik akademinis eksperimentas. Rust čia turi ką pasiūlyti. Cargo – Rust paketų valdiklis ir build sistema – yra vienas geriausių tokio tipo įrankių bet kurioje kalboje.

Cargo daro beveik viską: kuria naujus projektus, valdo priklausomybes, kompiliuoja kodą, paleidžia testus, generuoja dokumentaciją, publikuoja paketus. Ir visa tai su paprasta, intuityvia komandų eilute. cargo new, cargo build, cargo test – ir jūs jau produktyvūs.

Crates.io – oficiali Rust paketų repozitorija – turi daugiau nei 100,000 paketų. Nuo web framework’ų (Actix, Rocket, Axum) iki žemo lygio sistemų programavimo bibliotekų. Kokybė, žinoma, skiriasi, bet populiarūs paketai paprastai yra gerai prižiūrimi ir dokumentuoti.

Dokumentacija apskritai yra Rust stiprybė. Kiekvienas paketas gali turėti integruotą dokumentaciją, kuri generuojama iš kodo komentarų. cargo doc --open ir jūs naršote pilną savo projekto ir visų priklausomybių dokumentaciją naršyklėje.

IDE palaikymas taip pat neblogas. Rust-analyzer suteikia puikią patirtį VS Code, IntelliJ IDEA ir kituose redaktoriuose. Auto-completion, error highlighting, refactoring įrankiai – visa tai veikia gana gerai, nors kartais gali būti lėtokas dideliuose projektuose.

Mokymosi kreivė ir realybė

Būkime sąžiningi: Rust nėra lengva kalba išmokti. Jei ateinat iš Python, JavaScript ar net Java pasaulio, pradžia bus sudėtinga. Ownership sistema, lifetime’ai, trait’ai, macro’sai – visa tai reikalauja laiko ir praktikos.

Kompiliatorius pradžioje atrodys kaip griežtas mokytojas, kuris atmeta beveik kiekvieną jūsų bandymą. „Cannot move out of borrowed content”, „lifetime mismatch”, „trait bound not satisfied” – šie pranešimai taps jūsų kasdienybe pirmąsias savaites.

Bet štai įdomus dalykas: daugelis programuotojų praneša apie „aha!” momentą po kelių savaičių ar mėnesių. Staiga viskas ima dėliotis į vietas. Jūs pradedati suprasti, kodėl kompiliatorius skundžiasi, ir net pradedati vertinti jo pagalbą. Tas pats kodas, kurį anksčiau rašėte kitose kalbose, dabar atrodo pilnas potencialių problemų.

Praktinis patarimas: nepulkite iš karto rašyti sudėtingų projektų. Pradėkite nuo paprastų CLI įrankių, eksperimentuokite su ownership sistema, spręskite Advent of Code užduotis Rust kalba. Skaitykite „The Rust Programming Language” knygą (žinomą kaip „The Book”) – ji tikrai gera ir nemokama.

Taip pat naudokite Rust Playground – online aplinką, kur galite eksperimentuoti be jokios instaliacijos. Ir nebijokite klausti bendruomenėje – Rust community yra žinoma kaip viena draugiškiausių programavimo pasaulyje.

Kada Rust yra teisinga kalba jūsų projektui

Ne kiekvienas projektas reikalauja Rust. Jei kuriate paprastą CRUD aplikaciją su standartine verslo logika, Python su Django ar JavaScript su Node.js tikriausiai bus greitesnis ir paprastesnis pasirinkimas. Rust overhead’as čia neatsipirks.

Bet yra sričių, kur Rust tikrai spindi:

Sistemų programavimas – operacinės sistemos, device driver’iai, embedded sistemos. Čia Rust siūlo C/C++ alternatyvą su geresniu saugumu.

Tinklo serveriai ir mikroservisai – kai našumas ir mažas resource’ų naudojimas yra kritiniai. Actix-web framework’as yra vienas greičiausių web framework’ų bet kurioje kalboje.

CLI įrankiai – Rust programos kompiliuojasi į vieną executable failą be runtime dependencies. Ripgrep, exa, bat – populiarūs CLI įrankiai parašyti Rust.

WebAssembly – Rust turi puikų WASM palaikymą ir yra viena populiariausių kalbų šiai platformai.

Blockchain ir kriptografija – kai saugumas yra kritinis, o našumas svarbus. Solana, Polkadot ir kiti projektai naudoja Rust.

Įterptinės sistemos – Rust embedded ekosistema auga, ir jau galima rašyti firmware be C.

Jei jūsų projektas patenka į vieną iš šių kategorijų, Rust tikrai verta apsvarstyti. Taip, mokymosi kreivė stačioka, bet ilgalaikėje perspektyvoje investicija atsipirks per mažiau bug’ų, geresnį našumą ir lengvesnį palaikymą.

Ką Rust reiškia programavimo ateičiai

Rust įtaka jau jaučiama už jos pačios ribų. Kitos kalbos pradeda įtraukti ownership koncepcijas – Swift turi panašią sistemą, C++ gauna lifetime annotations. Rust įrodė, kad galima turėti ir saugumą, ir našumą, ir tai keičia lūkesčius.

Didžiosios tech kompanijos investuoja į Rust ne dėl mados. Jos investuoja, nes saugumo problemos kainuoja realius pinigus ir reputaciją. Kai Microsoft sako, kad 70% jų saugumo spragų yra memory safety problemos, ir Rust gali jas eliminuoti – tai rimtas verslo argumentas.

Ar Rust pakeis C ir C++ artimiausiu metu? Tikriausiai ne. Per daug legacy kodo, per daug investicijų į esamas sistemas. Bet naujiems projektams, kur anksčiau automatiškai būtų pasirinkta C++, dabar Rust tampa realiu kandidatu.

Ar Rust taps dominuojančia kalba? Sunku pasakyti. Bet viena aišku – Rust jau pakeitė diskusiją apie tai, ką programavimo kalba gali ir turėtų daryti. Idėja, kad kompiliatorius gali būti jūsų partneris, padedantis rašyti geresnį kodą, o ne tik kliūtis tarp jūsų ir vykdomojo failo, dabar atrodo akivaizdi.

Jei esate programuotojas, kuris rūpinasi kodo kokybe, našumu ir saugumu, Rust tikrai verta jūsų dėmesio. Taip, pradžia bus sudėtinga. Taip, kartais norėsis mesti kompiuterį pro langą, kai kompiliatorius atmes jūsų kodą dešimtą kartą. Bet kai įveiksite tą slenkstį, turėsite įrankį, kuris leis kurti greitą, saugų ir patikimą programinę įrangą. O tai, galų gale, ir yra programavimo esmė.

Daugiau

Server-side request forgery (SSRF) prevencija