Przejdź do głównej treści
eLearner.app
Moduł 3 · Lekcja 1 z 25/14 w kursie~15 min
Lekcje modułu (1/2)

Zasady Ownership

System zarządzania własnością (Ownership) to najbardziej charakterystyczna cecha języka Rust. Pozwala on na zagwarantowanie bezpieczeństwa pamięci bez potrzeby używania odśmiecacza pamięci (Garbage Collector) oraz bez konieczności ręcznego zwalniania pamięci sterty (heap) jak w C lub C++.

Trzy reguły własności (Ownership)

Zarządzanie pamięcią w języku Rust opiera się na trzech prostych regułach rygorystycznie kontrolowanych przez kompilator:

  1. Każda wartość w języku Rust ma zmienną zwaną jej właścicielem (owner).
  2. W danym momencie może istnieć tylko jeden właściciel.
  3. Kiedy właściciel wychodzi poza zakres (scope), wartość jest automatycznie usuwana.

Stos (Stack) vs Sterta (Heap)

Aby zrozumieć mechanizm własności, ważne jest, aby wiedzieć, gdzie znajdują się dane w pamięci:

  • Stos (Stack): przechowuje dane o stałym i znanym w momencie kompilacji rozmiarze (np. liczby całkowite, wartości logiczne). Dostęp do nich jest niezwykle szybki.
  • Sterta (Heap): przechowuje dane o dynamicznym i nieznanym w momencie kompilacji rozmiarze (np. String). Alokacja pamięci na stercie jest wolniejsza i wymaga zapisania wskaźnika (pointer) na stosie.

Koncepcja przeniesienia własności ("Move")

Kiedy przypisujemy zmienną przechowywaną na stercie do innej zmiennej, Rust przenosi (moves) własność wartości, unieważniając pierwszą zmienną:

Code
let s1 = String::from("ciao");
let s2 = s1; // La proprieta del testo si sposta a s2. s1 NON e piu utilizzabile!

// println!("{}", s1); // ERRORE DI COMPILAZIONE! s1 e "borrow of moved value"
println!("{}", s2); // Valido!

To zachowanie pozwala uniknąć błędu podwójnego zwalniania pamięci (double free error) przy wyjściu z zakresu, ponieważ tylko s2 zwolni pamięć.

Koncepcja klonowania ("Clone" - głębokie kopiowanie)

Jeśli zachodzi jawna potrzeba skopiowania całej zawartości obiektu String (zarówno wskaźników na stosie, jak i rzeczywistych danych tekstowych na stercie), możemy użyć metody .clone(). Duplikuje to całkowicie dane na stercie, dzięki czemu zarówno oryginalna, jak i nowa zmienna pozostają ważne, kosztem wydajności wynikającym z nowej alokacji pamięci:

Code
let s1 = String::from("ciao");
let s2 = s1.clone(); // Copia profonda. Entrambe le variabili rimangono valide!
println!("s1: {}, s2: {}", s1, s2);

Koncepcja kopiowania ("Copy")

W przypadku prostych typów przechowywanych w całości na stosie (takich jak liczby całkowite i32, wartości logiczne bool, znaki char), przypisanie wykonuje rzeczywistą automatyczną kopię powierzchowną, dzięki czemu obie zmienne pozostają ważne:

Code
let x = 5;
let y = x; // Copia il valore 5 nello stack. Entrambe le variabili sono utilizzabili!
println!("x: {}, y: {}", x, y); // Valido!

Spróbuj sam

Ćwiczenie#rust.m3.l1.e1
Próby: 0Ładowanie...

Zadeklaruj String s1 zawierający tekst 'hello'. Przypisz s1 do s2 (w celu przeniesienia własności). Na koniec wypisz s2 na ekranie za pomocą println!.

Ładowanie edytora...
Pokaż wskazówkę

Użyj `let s2 = s1;` do przeniesienia własności do `s2` i wypisz ją za pomocą `println!('{}', s2);`.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie#rust.m3.l1.e2
Próby: 0Ładowanie...

Zadeklaruj zmienną całkowitą x o wartości 5. Przypisz x do y (wykonując kopiowanie). Na koniec wypisz zarówno x, jak i y w tej samej instrukcji println!.

Ładowanie edytora...
Pokaż wskazówkę

Zapisz `let y = x;` a następnie wypisz obie wartości, np. `println!('{} {}', x, y);`.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie#rust.m3.l1.e3
Próby: 0Ładowanie...

Zadeklaruj zmienną String s1 o wartości "Rust". Utwórz głęboką kopię s1 w nowej zmiennej s2 za pomocą metody clone. Na koniec wypisz s1 oraz s2 oddzielone spacją za pomocą makra println!.

Ładowanie edytora...
Pokaż wskazówkę

Użyj `let s2 = s1.clone();` do głębokiego skopiowania wartości `s1` do `s2`, a następnie wypisz je za pomocą `println!("{} {}", s1, s2);`.

Rozwiązanie dostępne po 3 próbach