programowanieProgramista Backend

Jak działa mechanizm slice'ów w Rust, jakie typy slice istnieją, jak zapewniana jest bezpieczeństwo i co się wydarzy, gdy spróbujemy wyjść poza granice slice?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

Slice (fragment) w Rust to dynamiczna reprezentacja części kolekcji, której elementy są uporządkowane w pamięci sekwencyjnie. Typowym przykładem slice'a jest &[T] lub &mut [T]. Slice'y nie posiadają danych, a odnoszą się do zewnętrznego bufora. Wszystkie slice mają długość, która jest przechowywana razem z odniesieniem.

Główne typy:

  • &[T] — niemodyfikowalny slice
  • &mut [T] — modyfikowalny slice
  • Dla stringów: &str (faktycznie — slice bajtów, zawsze poprawny UTF-8)

Bezpieczeństwo slice'a jest zapewniane na poziomie kompilatora i runtime'u: wszelkie próby odwołania się do elementu poza granicami slice'a spowodują panikę (panic) w czasie wykonania, żaden niezdobny błąd w pamięci jak w C/C++ się nie wydarzy.

Przykład:

let v = vec![1, 2, 3, 4, 5]; let s: &[i32] = &v[1..4]; // slice, odnosi się do elementów 2, 3, 4 println!("{:?}", s); // [2, 3, 4] // s[3]; // panic! (wyjście poza granice)

Pytanie podchwytliwe

Czy slice'y mogą być puste? Czym różni się pusty slice od None?

Odpowiedź: Tak, slice mogą być puste (&[]), co oznacza odniesienie do części danych o zerowej długości, ale nie jest analogiczne do None. Pusty slice jest bezpieczny w użyciu, a Option<&[T]> służy do rozróżnienia "czy slice w ogóle istnieje".

Przykład:

let s: &[i32] = &[]; assert!(s.is_empty()); // Option<&[i32]> jest używane, jeśli slice może w ogóle nie istnieć.

Przykłady rzeczywistych błędów z powodu nieznajomości szczegółów tematu


Historia

W dużej usłudze logowania programista wziął slice według zakresu obliczanego dynamicznie z danych użytkownika. Przy błędnym obliczeniu slice'a (np. przy start > end lub jeśli end > len), kod działał w testach, ale w produkcji spowodował "panic" i awaryjnie zatrzymał proces podczas szczytu obciążenia.


Historia

W wewnętrznej bibliotece równoległego haszowania używano &mut [T] i równolegle kilka wątków brało różne slice tego samego tablicy. Jeden wątek zmieniał slice, a inny na tę samą pamięć brał inny slice. Program się kompilował, ale z powodu niewłaściwego podziału mogły wystąpić UB przez niebezpieczny kod (użycie unsafe), jeśli slice'y jednak się pokrywały.


Historia

W systemowym parserze pakietów sieciowych tworzono slice w sposób niebezpieczny (wskaźnik surowy i from_raw_parts). Programista zapomniał sprawdzić poprawność długości przychodzącego pakietu. W rezultacie próba odczytu poza granice spowodowała awarię aplikacji i podatność (potencjalny dostęp poza granicami), którą można było usunąć dzięki właściwemu zastosowaniu bezpiecznych slice'ów.