Learn Rust Coding · Lekcja

Praca z Rust w trybie unsafe

Dowiedzą się Państwo, kiedy i jak używać bloków `unsafe` do pomijania kontroli bezpieczeństwa Rust, co umożliwia korzystanie z FFI i niskopoziomowych operacji na pamięci.

Lekcja 3 z 311 kroki

Praca z Rust w trybie unsafe to bezpłatna lekcja Learn Rust Coding na CoddyKit. To lekcja 3 z 3. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Learn Rust Coding, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Learn Rust Coding zawiera 3 lekcji w sumie.

Niebezpieczny Rust: wprowadzenie

Witamy w świecie Unsafe Rust! Choć Rust słynie z gwarancji bezpieczeństwa pamięci, w określonych sytuacjach może być konieczne pominięcie niektórych kontroli.

Służy do tego słowo kluczowe unsafe. Nie jest ono sposobem na pisanie „złego” kodu, lecz narzędziem do zaawansowanych zastosowań wymagających bardziej bezpośredniej kontroli nad pamięcią lub sprzętem.

Pięć supermocy unsafe

Użycie bloku unsafe zapewnia dostęp do pięciu specjalnych operacji, których kompilator Rust zwykle zabrania:

  • Wyłuskiwanie wskaźników surowych: Bezpośredni dostęp do adresów pamięci.
  • Wywoływanie funkcji lub metod unsafe: Wykonywanie funkcji z warunkami wstępnymi, których kompilator nie potrafi zweryfikować.
  • Implementowanie traitów unsafe: Zadeklarowanie, że dany typ zachowuje określone niezmienniki.
  • Dostęp do zmiennych statycznych lub ich modyfikowanie: Globalne współdzielenie zmiennego stanu, z ryzykiem wyścigów danych.
  • Dostęp do pól union: Odczytywanie unii, której pole może nie odpowiadać typowi bieżących danych.

Wyłuskiwanie wskaźników surowych

Wskaźniki surowe to adresy pamięci pozbawione standardowych gwarancji bezpieczeństwa Rust. Można je tworzyć na podstawie referencji (&T, &mut T), ale ich wyłuskanie wymaga bloku unsafe.

Oznacza to, że to programista odpowiada za upewnienie się, że wskaźnik jest prawidłowy i wskazuje zaalokowaną pamięć.

fn main() {
  let mut num = 5;
  let r1 = &num as *const i32; // Immutable raw pointer
  let r2 = &mut num as *mut i32; // Mutable raw pointer

  unsafe { // The `unsafe` block starts here
    println!("r1 points to: {}", *r1);
    *r2 = 10; // Modify data through mutable raw pointer
    println!("r2 points to: {}", *r2);
  } // The `unsafe` block ends here

  println!("Num is now: {}", num);
}

Wywoływanie funkcji unsafe

Niektóre funkcje są oznaczone jako unsafe fn. Oznacza to, że mają warunki wstępne, których kompilator Rust nie może zagwarantować. Funkcja może na przykład oczekiwać prawidłowego adresu pamięci, ale nie potrafić go zweryfikować.

Wywołanie takich funkcji musi być umieszczone w bloku unsafe, co oznacza, że osoba wywołująca zapewnia spełnienie wszystkich warunków wstępnych.

unsafe fn dangerous_operation() {
  println!("This operation could be dangerous if preconditions aren't met!");
}

fn main() {
  println!("Attempting a dangerous operation...");
  unsafe { // Calling an unsafe function requires `unsafe`
    dangerous_operation();
  }
  println!("Operation completed.");
}

Zmienna statyczna mutowalna

Rust domyślnie uniemożliwia globalny zmienny stan, aby zapobiegać wyścigom danych. Można jednak deklarować zmienne statyczne mutowalne za pomocą static mut.

Dostęp do tych zmiennych lub ich modyfikowanie jest uznawane za unsafe, ponieważ wiele wątków może próbować uzyskać do nich dostęp jednocześnie, prowadząc do niezdefiniowanego zachowania. Za odpowiednią synchronizację odpowiada programista.

static mut COUNTER: i32 = 0; // A mutable static variable

fn add_to_counter(inc: i32) {
  unsafe { // Modifying `static mut` requires `unsafe`
    COUNTER += inc;
  }
}

fn main() {
  add_to_counter(5);
  unsafe { // Reading `static mut` also requires `unsafe`
    println!("COUNTER after first add: {}", COUNTER);
  }

  add_to_counter(10);
  unsafe {
    println!("COUNTER after second add: {}", COUNTER);
  }
}

Implementowanie traitów unsafe

Niektóre traity w Rust są oznaczone jako unsafe trait. Oznacza to, że ich implementowanie wymaga zachowania określonych niezmienników, których kompilator nie potrafi sprawdzić.

Typowym przykładem są traity Send i Sync, związane z bezpieczeństwem wątkowym. Jeśli implementują Państwo ręcznie unsafe trait, należy użyć unsafe impl Trait for Type i zagwarantować jego właściwości bezpieczeństwa.

Większość początkujących programistów spotyka się z tym rzadziej, ponieważ Rust często obsługuje te kwestie automatycznie lub za pomocą bezpiecznych abstrakcji.

Dostęp do pól unii

union to specjalny typ, który w danym momencie może przechowywać wartość *jednego* ze swoich wariantów, ale wszystkie warianty współdzielą to samo miejsce w pamięci. Przypomina unie z języka C.

Dostęp do pola union wymaga bloku unsafe, ponieważ kompilator nie może wiedzieć, które pole jest obecnie aktywne. Odczyt nieaktywnego pola może prowadzić do niezdefiniowanego zachowania lub błędnej interpretacji danych.

union Data {
  integer: u32,
  float: f32,
}

fn main() {
  let mut d = Data { integer: 42 }; // Initialize with an integer

  unsafe {
    // Accessing 'integer' is safe here
    println!("Integer value: {}", d.integer);

    // Accessing 'float' is unsafe, as 'integer' was initialized.
    // This would interpret the integer's bits as a float.
    // println!("Float value (unsafe): {}", d.float);
  }

  d.float = 3.14; // Now initialize with a float
  unsafe {
    // Accessing 'float' is safe now
    println!("Float value: {}", d.float);
  }
}

Praktyczne zastosowania unsafe

Choć z unsafe należy korzystać oszczędnie, jest ono niezbędne w kilku zaawansowanych zadaniach programistycznych:

  • Foreign Function Interface (FFI): Współpraca z kodem napisanym w innych językach (np. bibliotekami C/C++).
  • Optymalizacje wydajności: W niektórych sytuacjach pominięcie kontroli Rust może przynieść niewielki wzrost wydajności w krytycznych sekcjach (ale najpierw należy to zmierzyć!).
  • Programowanie systemowe niskiego poziomu: Pisanie systemów operacyjnych, sterowników urządzeń lub systemów wbudowanych.
  • Budowanie bezpiecznych abstrakcji: Implementowanie bezpiecznych struktur danych lub interfejsów API na podstawie niebezpiecznych prymitywów (np. sposób, w jaki zbudowany jest Vec).

Odpowiedzialność związana z unsafe

Używając unsafe, biorą Państwo na siebie odpowiedzialność za ręczne zachowanie gwarancji bezpieczeństwa Rust. Oznacza to:

  • Upewnianie się, że pamięć jest prawidłowa i odpowiednio wyrównana.
  • Zapobieganie wyścigom danych podczas pracy ze współdzielonym zmiennym stanem.
  • Unikanie wiszących wskaźników oraz błędów use-after-free.
  • Upewnianie się, że wszystkie warunki wstępne funkcji są spełnione.

Niewłaściwe użycie unsafe może prowadzić do niezdefiniowanego zachowania, czyli najgorszego rodzaju błędu, który może być bardzo trudny do zdebugowania.

Sprawdzenie wiedzy o unsafe

Która z poniższych operacji wymaga w Rust bloku unsafe?

Podsumowanie: Unsafe Rust

Poznali Państwo Unsafe Rust — zaawansowaną funkcję umożliwiającą pomijanie niektórych kontroli bezpieczeństwa wykonywanych przez Rust w czasie kompilacji, w określonych zaawansowanych sytuacjach.

Należy pamiętać, że unsafe nie wyłącza całkowicie borrow checkera, lecz przenosi na programistę pełną odpowiedzialność za bezpieczeństwo pamięci i zachowanie niezmienników. Należy używać go rozważnie i z najwyższą ostrożnością, zazwyczaj w przypadku FFI, niskopoziomowych optymalizacji lub budowania bezpiecznych abstrakcji.

Bezpłatny start

Ucz się Rust dzięki korepetycjom AI — za darmo

Pisz i uruchamiaj kod w przeglądarce, otrzymuj natychmiastową pomoc od korepetytora AI dostępnego 24/7 i kontynuuj naukę w sieci lub w aplikacji.

Kursy
39
Lekcje
144

Często zadawane pytania

Czy lekcja „Praca z Rust w trybie unsafe” jest bezpłatna?

Tak — pełny tekst „Praca z Rust w trybie unsafe” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Learn Rust Coding, przejdź na CoddyKit PRO. Kurs Learn Rust Coding zawiera 3 lekcji w sumie.

Co nauczysz się w „Praca z Rust w trybie unsafe”?

Dowiedzą się Państwo, kiedy i jak używać bloków `unsafe` do pomijania kontroli bezpieczeństwa Rust, co umożliwia korzystanie z FFI i niskopoziomowych operacji na pamięci. Ćwiczysz Learn Rust Coding z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Learn Rust Coding?

Nie wymagamy żadnego doświadczenia. Learn Rust Coding w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 3 z 3.

Ile czasu zajmuje lekcja „Praca z Rust w trybie unsafe”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Learn Rust Coding?

Tak. Każda lekcja Learn Rust Coding zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Makra deklaratywne (`macro_rules!`)
  2. Makra proceduralne: Derive, Function
  3. Praca z Rust w trybie unsafe
← Powrót do Learn Rust Coding