Od eksperymentu Graydona Hoare’a w 2006 roku po współczesny ekosystem Rust — jak bezpieczeństwo pamięci bez garbage collectora przekształciło programowanie systemowe.

Ten artykuł opowiada skupioną historię powstania: jak osobisty eksperyment Graydona Hoare’a przekształcił się w Rust i dlaczego wybory projektowe Rust miały wystarczającą wagę, by zmienić oczekiwania wobec programowania systemowego.
„Programowanie systemowe” działa blisko maszyny — i blisko ryzyka w twoim produkcie. Pojawia się w przeglądarkach, silnikach gier, komponentach systemu operacyjnego, bazach danych, sieciach i oprogramowaniu wbudowanym — miejscach, gdzie zazwyczaj potrzebujesz:
Historycznie to zestawienie kierowało zespoły ku C i C++, z dodatkowymi zasadami, przeglądami i narzędziami redukującymi błędy związane z pamięcią.
Nagłówek Rust jest prosty do powiedzenia, trudny do zrealizowania:
Bezpieczeństwo pamięci bez garbage collectora.
Rust ma na celu zapobieganie typowym awariom jak use-after-free, double-free czy wielu rodzajom wyścigów danych — bez polegania na runtime’owym mechanizmie, który okresowo zatrzymuje program, by odzyskać pamięć. Zamiast tego Rust przenosi dużą część tej pracy na czas kompilacji przez mechanizmy własności i wypożyczania.
Dostaniesz historię (od wczesnych pomysłów po zaangażowanie Mozilli) oraz kluczowe koncepcje (własność, wypożyczanie, lifetimes, safe vs. unsafe) wyjaśnione prostym językiem.
Nie otrzymasz pełnego kursu Rust, kompletnego przeglądu składni ani krok po kroku konfiguracji projektu. Traktuj to jako „dlaczego” stojące za projektem Rust, z wystarczającą ilością przykładów, by te idee stały się namacalne.
Uwaga autora: pełny tekst ma około 3 000 słów, pozostawiając miejsce na krótkie przykłady bez przemiany w podręcznik referencyjny.
Rust nie powstał jako język zaprojektowany przez komitet, który miał „pokonać” C++. Zaczęło się jako osobisty eksperyment Graydona Hoare’a w 2006 roku — praca, którą prowadził niezależnie, zanim przykuła szerszą uwagę. Ten początek ma znaczenie: wiele wczesnych decyzji projektowych wygląda jak próby rozwiązania codziennych bóli, a nie zwyciężenia teorii języków.
Hoare badał, jak pisać niskopoziomowe, wydajne oprogramowanie bez polegania na garbage collectoru — a jednocześnie unikać najczęstszych przyczyn awarii i luk bezpieczeństwa w C i C++. Napięcie jest znajome dla programistów systemowych:
Kierunek Rust — „bezpieczeństwo pamięci bez GC” — nie był początkowo hasłem marketingowym. Był celem projektowym: zachować cechy wydajnościowe odpowiednie dla pracy systemowej, ale uczynić wiele kategorii błędów pamięci trudnymi do wyrażenia.
To naturalne pytanie, dlaczego nie wystarczy lepszy kompilator dla C/C++. Narzędzia typu analiza statyczna, sanitizery i bezpieczne biblioteki zapobiegają wielu problemom, ale zwykle nie mogą zagwarantować bezpieczeństwa pamięci. Języki podstawowe dopuszczają wzorce, które trudno — albo niemożliwe — w pełni wyegzekwować z zewnątrz.
Zakład Rust polegał na przeniesieniu kluczowych reguł do języka i systemu typów, tak by bezpieczeństwo stało się domyślnym wynikiem, z możliwością ręcznej kontroli w wyraźnie oznaczonych miejscach ucieczki.
Niektóre szczegóły wczesnych dni Rust krążą jako anegdoty (często powtarzane w wykładach i wywiadach). Przy opowiadaniu tej historii warto oddzielić szeroko udokumentowane kamienie milowe — jak start w 2006 roku i późniejsze przyjęcie projektu w Mozilla Research — od osobistych wspomnień i wtórnych powtórzeń.
Jako źródła pierwotne warto szukać wczesnej dokumentacji i notatek projektowych, wystąpień i wywiadów Graydona Hoare’a oraz postów z ery Mozilla/Servo opisujących, dlaczego projekt został przejęty i jak sformułowano jego cele. Sekcja „dalsza lektura” może skierować czytelników do tych oryginałów (zobacz /blog po powiązane materiały).
Programowanie systemowe często oznacza pracę blisko sprzętu. Ta bliskość sprawia, że kod jest szybki i efektywny w użyciu zasobów. To też powoduje, że błędy pamięci są szczególnie bolesne.
Kilka klasycznych błędów pojawia się w kółko:
Te błędy nie zawsze są oczywiste. Program może „działać” tygodniami, a potem zawiesić się tylko przy rzadkim wzorcu wejścia lub timingowym zdarzeniu.
Testowanie dowodzi, że coś działa dla przypadków, które wypróbowałeś. Błędy pamięci często ukrywają się w przypadkach, których nie testowałeś: nietypowe dane wejściowe, inny sprzęt, drobne zmiany w czasie wykonywania albo nowa wersja kompilatora. Mogą też być niedeterministyczne — szczególnie w programach wielowątkowych — więc błąd znika, gdy dodasz logowanie lub podłączysz debuger.
Kiedy pamięć idzie nie tak, nie dostajesz tylko czystego błędu. Dostajesz zniszczony stan, nieprzewidywalne awarie i podatności bezpieczeństwa, których aktywnie szukają atakujący. Zespoły wydają ogromne zasoby na ściganie przecenionych błędów, trudnych do odtworzenia i jeszcze trudniejszych do zdiagnozowania.
Oprogramowanie niskiego poziomu nie zawsze „może zapłacić” za bezpieczeństwo ciężkimi sprawdzeniami w czasie działania czy ciągłym skanowaniem pamięci. Celem jest raczej wypożyczenie narzędzia z warsztatu: możesz z niego korzystać, ale zasady muszą być jasne — kto je trzyma, kto może się nimi dzielić i kiedy trzeba je zwrócić. Języki systemowe tradycyjnie oddawały te zasady w ręce ludzkiej dyscypliny. Historia powstania Rust zaczyna się od zakwestionowania tego kompromisu.
Garbage collection to powszechny sposób na zapobieganie błędom pamięci: runtime śledzi obiekty i automatycznie odzyskuje nieosiągalne. To eliminuje całe kategorie problemów — use-after-free, double free i wiele wycieków — ponieważ program nie może „zapomnieć” o sprzątaniu w ten sam sposób.
GC nie jest „zły”, ale zmienia profil wydajności programu. Większość kolektorów wprowadza kombinację:
Dla wielu aplikacji — backendów webowych, oprogramowania biznesowego, narzędzi — te koszty są akceptowalne lub niezauważalne. Nowoczesne GC są świetne i zwiększają produktywność programistów.
W programowaniu systemowym najgorszy przypadek często ma największe znaczenie. Silnik przeglądarki potrzebuje płynnego renderowania; kontroler wbudowany może mieć ścisłe ograniczenia czasowe; serwer niskich opóźnień może być dostrojony tak, by utrzymywać ogonowe opóźnienia pod kontrolą. W takich środowiskach „zwykle szybkie” może być mniej wartościowe niż „konsekwentnie przewidywalne”.
Duża obietnica Rust brzmiała: zachowaj kontrolę jak w C/C++ nad pamięcią i układem danych, ale zapewnij bezpieczeństwo pamięci bez polegania na garbage collectorze. Cel to przewidywalne charakterystyki wydajności – a jednocześnie uczynienie bezpiecznego kodu domyślnym.
To nie jest teza, że GC jest gorszy. To założenie, że istnieje istotna i ważna przestrzeń pośrednia: oprogramowanie, które potrzebuje niskopoziomowej kontroli i nowoczesnych gwarancji bezpieczeństwa.
Własność to najprostsza wielka idea Rust: każda wartość ma pojedynczego właściciela, odpowiedzialnego za jej posprzątanie, gdy nie jest już potrzebna.
Ta jedna reguła zastępuje wiele ręcznych rozliczeń „kto to zwalnia?” które programiści C/C++ często noszą w głowie. Zamiast polegać na dyscyplinie, Rust sprawia, że sprzątanie jest przewidywalne.
Kiedy kopiujesz coś, masz dwie niezależne wersje. Kiedy przenosisz coś, przekazujesz oryginał — po ruchu stara zmienna nie może już tego używać.
Rust traktuje wiele wartości alokowanych na stercie (jak stringi, bufory czy wektory) jako domyślnie przenoszone. Bezwzględne kopiowanie może być kosztowne i, co ważniejsze, mylące: jeśli dwie zmienne myślą, że „właściciele” tej samej alokacji, tworzysz warunki do błędów pamięci.
Oto pomysł w małym pseudo-kodzie:
buffer = make_buffer()
ownerA = buffer // ownerA owns it
ownerB = ownerA // move ownership to ownerB
use(ownerA) // not allowed: ownerA no longer owns anything
use(ownerB) // ok
// when ownerB ends, buffer is cleaned up automatically
(Powyższy blok pozostaje nieprzetłumaczony w treści kodu.)
Ponieważ zawsze jest dokładnie jeden właściciel, Rust dokładnie zna moment, kiedy wartość powinna zostać posprzątana: gdy jej właściciel wychodzi poza zakres. To oznacza automatyczne zarządzanie pamięcią (nie wywołujesz wszędzie free()), bez potrzeby garbage collectora okresowo skanującego program i odzyskującego nieużywaną pamięć.
Ta zasada własności blokuje dużą klasę klasycznych problemów:
Model własności Rust nie tylko zachęca do bezpieczniejszych praktyk — czyni wiele niebezpiecznych stanów niemożliwymi do wyrażenia, co jest fundamentem dla pozostałych funkcji bezpieczeństwa Rust.
Własność wyjaśnia, kto „właścicielem” wartości. Wypożyczanie wyjaśnia, jak inne części programu mogą tymczasowo korzystać z tej wartości, nie odbierając jej właścicielowi.
Gdy wypożyczasz coś w Rust, otrzymujesz referencję do tego. Oryginalny właściciel pozostaje odpowiedzialny za zwolnienie pamięci; wypożyczający ma tylko pozwolenie na użycie tego przez pewien czas.
Rust ma dwa rodzaje wypożyczeń:
&T): dostęp tylko do odczytu.&mut T): dostęp do odczytu i zapisu.Centralna zasada wypożyczania Rust jest prosta do wypowiedzenia i potężna w praktyce:
Ta reguła zapobiega powszechnej klasie błędów: jednej części programu czytającej dane, podczas gdy inna część je zmienia pod spodem.
Referencja jest bezpieczna tylko wtedy, gdy nigdy nie żyje dłużej niż to, na co wskazuje. Rust nazywa ten okres lifetime — czasem, w którym referencja jest gwarantowanie ważna.
Nie potrzebujesz skomplikowanej formalności, by używać tej idei: referencja nie może pozostać po tym, jak właściciel zniknął.
Rust egzekwuje te reguły na etapie kompilacji przez borrow checker. Zamiast liczyć, że testy wyłapią złą referencję lub ryzykowną mutację, Rust odmawia kompilacji kodu, który mógłby błędnie użyć pamięci.
Pomyśl o współdzielonym dokumencie:
Współbieżność to miejsce, gdzie błędy „działa u mnie” lubią się ukrywać. Gdy dwa wątki działają jednocześnie, mogą wchodzić w zaskakujące interakcje — zwłaszcza jeśli dzielą dane.
Data race występuje, gdy:
Skutek to nie tylko „niepoprawny wynik”. Race może uszkadzać stan, powodować awarie programu albo tworzyć luki bezpieczeństwa. Co gorsza, może być przerywany — błąd znika, gdy dodasz logowanie lub uruchomisz debuger.
Rust zajmuje nietypowe stanowisko: zamiast ufać, że każdy programista pamięta reguły za każdym razem, próbuje uczynić wiele niebezpiecznych wzorców współbieżności nieprzedstawialnymi w bezpiecznym kodzie.
Na wysokim poziomie zasady własności i wypożyczania nie ograniczają się do kodu jednowątkowego. Kształtują też to, co można bezpiecznie współdzielić między wątkami. Jeśli kompilator nie potrafi udowodnić, że współdzielenie jest skoordynowane, nie pozwoli skompilować takiego kodu.
To, co ludzie opisują jako „bezpieczna współbieżność” w Rust, oznacza, że dalej piszesz programy współbieżne, ale cała kategoria błędów typu „ups, dwa wątki zapisały to samo” jest złapana przed uruchomieniem programu.
Wyobraź sobie dwa wątki inkrementujące ten sam licznik:
W Rust nie możesz po prostu dać mutowalnego dostępu do tej samej wartości wielu wątkom w bezpiecznym kodzie. Kompilator zmusza do wyrażenia zamiaru jawnie — zwykle przez użycie prymitywów synchronizacji (np. umieszczenie stanu za zamkiem) lub przez komunikację wiadomościami.
Rust nie zabrania niskopoziomowych sztuczek współbieżności. On je kwarantannuje. Jeśli naprawdę musisz zrobić coś, czego kompilator nie potrafi zweryfikować, możesz użyć bloków unsafe, które działają jak etykiety ostrzegawcze: „tu wymagana jest ludzka odpowiedzialność”. To rozdzielenie utrzymuje większość kodu w bezpiecznym podzbiorze, a jednocześnie pozwala na moc systemową, gdy jest to uzasadnione.
Reputacja Rust jako bezpiecznego może brzmieć absolutnie, ale dokładniej jest powiedzieć, że Rust czyni granice między bezpiecznym i niebezpiecznym programowaniem wyraźnymi — i łatwiejszymi do audytu.
Większość kodu to „safe Rust”. Tutaj kompilator wymusza reguły, które zapobiegają typowym błędom pamięci: use-after-free, double free, wiszące wskaźniki i data races. Nadal możesz napisać błędną logikę, ale przez zwykłe cechy języka nie możesz przypadkowo naruszyć bezpieczeństwa pamięci.
Ważne: safe Rust nie znaczy „wolniejszy Rust”. Wiele wysokowydajnych programów pisze się w całości w safe Rust, bo kompilator może agresywnie optymalizować, gdy może ufać, że reguły są spełnione.
unsafe istnieje, ponieważ programowanie systemowe czasem potrzebuje możliwości, których kompilator nie potrafi ogólnie udowodnić jako bezpieczne. Typowe powody to:
Użycie unsafe nie wyłącza wszystkich kontroli. Pozwala tylko na niewielki zestaw operacji (np. dereferencję surowych wskaźników), które normalnie są zabronione.
Rust zmusza do oznaczania bloków i funkcji unsafe, co sprawia, że ryzyko jest widoczne w przeglądzie kodu. Powszechną praktyką jest trzymanie małego „bezpiecznego jądra” w unsafe, otoczonego bezpiecznym API, tak by większość programu pozostała w safe Rust, a niewielki, dobrze zdefiniowany fragment dbał o niezbędne inwarianty.
Traktuj unsafe jak narzędzie:
unsafe małe i zlokalizowane.unsafe.Dobrze użyte, unsafe w Rust staje się kontrolowanym interfejsem do tych części programowania systemowego, które wciąż wymagają ręcznej precyzji — bez utraty korzyści bezpieczeństwa w reszcie kodu.
Rust nie stał się „prawdziwy” tylko dlatego, że miał sprytne pomysły na papierze — stał się prawdziwy, bo Mozilla postawiła te pomysły pod presją.
Mozilla Research szukała sposobów budowy wydajnych komponentów przeglądarki z mniejszą liczbą błędów bezpieczeństwa. Silniki przeglądarek są niezwykle złożone: parsują niezaufane dane, zarządzają ogromnymi ilościami pamięci i działają w wysoko współbieżnych warunkach. To powoduje, że błędy pamięci i warunki wyścigu są zarówno powszechne, jak i kosztowne.
Wspieranie Rust zgadzało się z tym celem: zachować szybkość programowania systemowego przy redukcji całych klas podatności. Zaangażowanie Mozilli wysłało też sygnał, że Rust to nie tylko osobisty eksperyment Graydona Hoare’a, ale język, który można przetestować na jednym z najtrudniejszych kodów na świecie.
Servo — eksperymentalny silnik przeglądarki — stał się miejscem wysokoprofilowego sprawdzenia Rust w skali. Chodziło nie o „wygranie” rynku przeglądarek. Servo był laboratorium, gdzie funkcje języka, diagnostyka kompilatora i narzędzia były oceniane w realnych ograniczeniach: czasy budowy, wsparcie wieloplatformowe, doświadczenie deweloperskie, strojenie wydajności i poprawność przy równoległości.
Co ważniejsze, Servo pomógł ukształtować ekosystem wokół języka: biblioteki, narzędzia budowania, konwencje i praktyki debugowania, które mają znaczenie, gdy przechodzisz poza programy demonstracyjne.
Projekty w świecie rzeczywistym tworzą pętle informacji zwrotnej, których projekt języka nie może udawać. Gdy inżynierowie natrafiali na tarcia — niejasne komunikaty błędów, brakujące biblioteki, niezręczne wzorce — te bolączki szybko wychodziły na światło dzienne. Z czasem to stałe napięcie pomogło Rustowi przekształcić się z obiecującej koncepcji w coś, czemu zespoły mogły zaufać przy tworzeniu dużego, krytycznego oprogramowania.
Jeśli chcesz zbadać dalszą ewolucję Rust po tej fazie, zobacz /blog/rust-memory-safety-without-gc.
Rust znajduje się w przestrzeni pośredniej: dąży do wydajności i kontroli oczekiwanej od C i C++, ale stara się wyeliminować dużą klasę błędów, które te języki często pozostawiają dyscyplinie, testom i przypadkowi.
W C i C++ deweloperzy zarządzają pamięcią bezpośrednio — alokują, zwalniają i dbają, by wskaźniki pozostawały ważne. Ta wolność jest potężna, ale też łatwo wprowadza use-after-free, double-free, przepełnienia bufora i subtelne błędy żywotności. Kompilator zwykle ufa programiście.
Rust odwraca tę relację. Nadal masz niskopoziomową kontrolę (stos vs sterta, przewidywalne układy, jawne transfery własności), ale kompilator egzekwuje reguły dotyczące tego, kto jest właścicielem wartości i jak długo referencje mogą żyć. Zamiast „uważaj na wskaźniki”, Rust mówi „udowodnij kompilatorowi bezpieczeństwo”, i nie skompiluje kodu, który mógłby złamać te gwarancje w safe Rust.
Języki z garbage collectorem (jak Java, Go, C# czy wiele języków skryptowych) zamieniają ręczne zarządzanie pamięcią na wygodę: obiekty są zwalniane automatycznie, gdy stają się nieosiągalne. To może bardzo zwiększyć produktywność.
Obietnica Rust — „bezpieczeństwo pamięci bez GC” — oznacza, że nie płacisz za runtime’owy kolektor, co pomaga, gdy potrzebujesz ścisłej kontroli nad opóźnieniami, zużyciem pamięci, czasem startu albo działasz w środowiskach o ograniczonych zasobach. Kosztem jest modelowanie własności jawnie i pozwolenie kompilatorowi na egzekwowanie go.
Rust może wydawać się trudniejszy na początku, bo uczy nowego sposobu myślenia: myślisz w kategoriach własności, wypożyczania i lifetimes, a nie tylko „przekaż wskaźnik i miej nadzieję, że będzie ok”. Wczesne tarcia często pojawiają się przy modelowaniu współdzielonego stanu lub złożonych grafów obiektów.
Rust błyszczy w zespołach budujących oprogramowanie wrażliwe na bezpieczeństwo i krytyczne wydajnościowo — przeglądarki, sieci, kryptografia, wbudowane systemy, backendy z rygorystycznymi wymaganiami niezawodności. Jeśli twój zespół ceni najszybszą iterację ponad niskopoziomową kontrolę, język z GC może wciąż być lepszym wyborem.
Rust nie jest uniwersalnym zamiennikiem; to mocna opcja, gdy chcesz wydajności klasy C/C++ z gwarancjami bezpieczeństwa, na których można polegać.
Rust nie zdobył uwagi, będąc „ładniejszym C++”. Zmienił dyskusję, nalegając, że kod niskiego poziomu może być jednocześnie szybki, bezpieczny pamięciowo i jawny co do kosztów.
Przed Rustem zespoły traktowały błędy pamięci jako podatek za wydajność, polegając na testach, przeglądach i naprawach po incydentach, by zarządzać ryzykiem. Rust postawił inny zakład: zakoduj common rules (kto jest właścicielem danych, kto może je mutować, kiedy muszą być ważne) w języku, aby całe kategorie błędów były odrzucane na etapie kompilacji.
Ta zmiana miała znaczenie, bo nie wymagała od programistów bycia „idealnymi”. Prosiła ich o bycie jasnymi — a potem pozwalała kompilatorowi egzekwować tę jasność.
Wpływ Rust widać w mieszance sygnałów, nie jednym nagłówku: rosnące zainteresowanie firm wysyłających wydajnościowe oprogramowanie, zwiększona obecność na uczelniach oraz narzędzia, które przestają być „projektami badawczymi” i stają się „narzędziami dnia codziennego” (zarządzanie pakietami, formatowanie, linting i workflow dokumentacyjny działające od ręki).
To nie znaczy, że Rust zawsze jest najlepszym wyborem — ale oznacza to, że bezpieczeństwo-domyślnie stało się realistycznym oczekiwaniem, a nie luksusem.
Rust jest często oceniany dla:
„Nowy standard” nie oznacza, że każdy system zostanie przepisany na Rust. Oznacza, że poprzeczka się przesunęła: zespoły coraz częściej pytają dlaczego akceptować domyślnie niebezpieczne ustawienia pamięci, skoro nie musimy? Nawet gdy Rust nie zostanie przyjęty, jego model skłonił ekosystem do wartościowania bezpieczniejszych API, jaśniejszych inwariantów i lepszych narzędzi do poprawności.
Jeśli chcesz więcej inżynierskich historii zza kulis, przeglądaj /blog po powiązane wpisy.
Historia powstania Rust ma prostą linię: projekt jednego człowieka (eksperyment Graydona Hoare’a) zderzył się z uporczywym problemem programowania systemowego, a rozwiązanie okazało się jednocześnie rygorystyczne i praktyczne.
Rust przeformułował kompromis, który wielu deweloperów uważało za nieunikniony:
Praktyczna zmiana to nie tylko „Rust jest bezpieczniejszy”. To, że bezpieczeństwo może być domyślną właściwością języka, a nie dyscypliną egzekwowaną przez przeglądy kodu i testy.
Jeśli jesteś ciekawy, nie potrzebujesz wielkiego przepisywania, aby poczuć, jak działa Rust.
Zacznij od małych rzeczy:
Jeśli chcesz łagodniejszej ścieżki, wybierz jedno „cienkie wycinki” celu — na przykład „odczytaj plik, przetwórz, zapisz wynik” — i skup się na czytelności kodu zamiast na sprytnych sztuczkach.
Gdy prototypujesz komponent Rust w większym produkcie, pomocne bywa przyspieszenie otaczających części (UI administracyjne, dashboardy, warstwa zarządzania) podczas gdy rdzeń systemowy trzymasz rygorystycznie. Platformy takie jak Koder.ai mogą przyspieszyć takie „klejące” prace przez chat-driven workflow — pozwalając wygenerować front-end React, backend w Go i schemat PostgreSQL szybko, a następnie eksportować źródła i integrować z serwisem Rust przez jasne granice.
Jeśli chcesz drugi wpis, co byłoby najbardziej przydatne?
Odpowiedz ze swoim kontekstem (co budujesz, jakiego języka teraz używasz i co optymalizujecie), a dopasuję kolejny rozdział do tego.
Programowanie systemowe to praca blisko sprzętu i obszarów produktu o wysokim ryzyku — jak silniki przeglądarek, bazy danych, komponenty systemu operacyjnego, sieci i oprogramowanie wbudowane.
Zwykle wymaga to przewidywalnej wydajności, niskopoziomowej kontroli nad pamięcią/zasobami oraz wysokiej niezawodności, ponieważ awarie i błędy bezpieczeństwa są tutaj szczególnie kosztowne.
To oznacza, że Rust dąży do zapobiegania typowym błędom pamięci (jak use-after-free czy double-free) bez polegania na runtime’owym garbage collectoru.
Zamiast tego wiele kontroli bezpieczeństwa przenosi się na czas kompilacji dzięki regułom własności i wypożyczania.
Narzędzia takie jak sanitizery i analizatory statyczne wykrywają wiele problemów, ale generalnie nie potrafią zagwarantować bezpieczeństwa pamięci, gdy język pozwala na niebezpieczne wzorce wskaźników i żywotności.
Rust wprowadza kluczowe reguły do języka i systemu typów, dzięki czemu kompilator może odrzucać całe kategorie błędów domyślnie, pozostawiając jednocześnie wyraźne drzwiczki awaryjne, gdy są potrzebne.
Garbage collector może wprowadzać narzut w czasie działania i, co ważniejsze w niektórych zastosowaniach systemowych, mniej przewidywalne opóźnienia (np. pauzy zbierania).
W dziedzinach takich jak silniki przeglądarek, sterowniki czasu rzeczywistego czy usługi niskich opóźnień, krytyczne jest zachowanie deterministycznego zachowania w najgorszym przypadku — dlatego Rust celuje w bezpieczeństwo przy zachowaniu przewidywalnej wydajności.
Własność oznacza, że każda wartość ma dokładnie jednego „odpowiedzialnego” właściciela. Kiedy właściciel wychodzi poza zakres (scope), wartość jest automatycznie sprzątana.
Daje to przewidywalne zarządzanie zasobami i zapobiega sytuacjom, w których dwa miejsca w kodzie myślą, że powinny zwolnić tę samą pamięć.
Ruch (move) przepisuje własność z jednej zmiennej na drugą; oryginalna zmienna nie może już używać tej wartości.
To zapobiega przypadkom „dwóch właścicieli jednej alokacji”, będącym częstą przyczyną double-free i use-after-free w językach z ręcznym zarządzaniem pamięcią.
Wypożyczanie (borrowing) pozwala tymczasowo używać wartości przez referencje bez przejmowania własności.
Główna zasada brzmi: wielu czytelników albo jeden piszący — możesz mieć wiele współdzielonych referencji (&T) lub jedną mutowalną (&mut T), ale nie oba jednocześnie. To zapobiega błędom polegającym na jednoczesnym odczycie i modyfikacji tej samej pamięci.
Żywotność (lifetime) to „jak długo referencja jest ważna”. Rust wymaga, by referencje nigdy nie żyły dłużej niż dane, na które wskazują.
Borrow checker sprawdza to na etapie kompilacji, dlatego kod mogący prowadzić do wiszących referencji zostanie odrzucony zanim program zostanie uruchomiony.
Wyścig danych to sytuacja, gdy wiele wątków jednocześnie ma dostęp do tej samej pamięci, przynajmniej jedno z tych dostępów to zapis, i brak jest koordynacji.
Zasady własności i wypożyczania w Rust ograniczają bezpieczne wzorce współdzielenia: jeśli kompilator nie może udowodnić, że dostęp jest skoordynowany, kod nie skompiluje się. To przenosi wiele klas błędów współbieżności do etapu kompilacji.
Większość kodu to „bezpieczny Rust” (safe Rust), gdzie kompilator wymusza reguły zapobiegające błędom pamięci.
unsafe to widoczna w kodzie furtka dla operacji, które kompilator nie może sam zweryfikować (np. niektóre wywołania FFI czy niskopoziomowe operacje). Dobrym zwyczajem jest ograniczać unsafe do małych, dobrze udokumentowanych fragmentów i owijać je bezpiecznym API.