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

Referencje i Borrowing

W języku Rust ciągłe przekazywanie własności (ownership) zmiennej do funkcji i jej zwracanie z powrotem może być bardzo niewygodne. Aby rozwiązać ten problem, Rust wykorzystuje referencje (references).

Tworzenie referencji do wartości nazywane jest pożyczaniem (Borrowing).

Referencje niezmienne

Referencję deklaruje się poprzez umieszczenie symbolu & przed typem lub zmienną. Domyślnie referencje są niezmienne (immutabili): pozwalają na odczyt wartości, ale nie na jej modyfikację.

Code
fn main() {
    let s1 = String::from("hello");

    // Passiamo un riferimento a s1, non l'ownership
    let len = calculate_length(&s1);

    // s1 è ancora utilizzabile qui!
    println!("La lunghezza di '{}' è {}.", s1, len);
}

fn calculate_length(s: &String) -> usize { // s è un riferimento a una String
    s.len()
} // Qui s esce dallo scope, ma poichè non possiede il valore, non succede nulla

Referencje modyfikowalne

Jeśli potrzebujesz zmodyfikować pożyczoną wartość, musisz użyć referencji modyfikowalnej za pomocą &mut. Oryginalna zmienna również musi być zadeklarowana jako modyfikowalna za pomocą mut:

Code
fn main() {
    let mut s = String::from("hello");

    // Passiamo un riferimento mutabile
    change(&mut s);

    println!("{}", s); // Stampa "hello, world"
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

Złote zasady pożyczania (Borrowing)

Aby zapobiec uszkodzeniom pamięci i wyścigom danych (data races) w czasie kompilacji, Rust narzuca dwie fundamentalne reguły:

  1. Możesz mieć dowolną liczbę niezmiennych referencji (&T) do danej wartości w tym samym czasie.
  2. ALBO możesz mieć dokładnie jedną modyfikowalną referencję (&mut T) do wartości w danym momencie.

W żadnym wypadku nie wolno mieszać referencji niezmiennych i modyfikowalnych dla tej samej wartości w tym samym zakresie (scope):

Code
let mut s = String::from("hello");

let r1 = &s; // Valido
let r2 = &s; // Valido
// let r3 = &mut s; // ERRORE DI COMPILAZIONE! Non puoi creare &mut s se s è già presa in prestito come immutabile

Zakres (Scope) referencji i Non-Lexical Lifetimes (NLL)

Dawniej zakres ważności referencji trwał obowiązkowo do końca bloku, w którym została utworzona. Dziś kompilator Rusta jest inteligentniejszy dzięki Non-Lexical Lifetimes (NLL): zakres referencji kończy się w ostatniej linii, w której jest ona używana, a niekoniecznie na końcu bloku.

Dzięki temu poniższy kod jest w pełni poprawny:

Code
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} e {}", r1, r2); // Ultimo uso di r1 e r2. I riferimenti immutabili scadono qui!

let r3 = &mut s; // Valido! Nessun riferimento immutabile è attivo a questo punto

Spróbuj sam

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

Przekaż niezmienną referencję s1 do funkcji calculate_length przy użyciu symbolu &.

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

Zastąp `/* TODO \_/`zapisem`&s1`, aby przekazać niezmienną referencję do ciągu znaków.

Rozwiązanie dostępne po 3 próbach

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

Uczyń zmienną s modyfikowalną (let mut s) i przekaż modyfikowalną referencję (&mut s) do funkcji change, aby umożliwić jej modyfikację ciągu znaków.

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

Użyj `let mut s`zamiast`let s`i wywołaj`change(&mut s);`.

Rozwiązanie dostępne po 3 próbach

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

Zadeklaruj zmienną s zawierającą String::from("Rust"). Utwórz dwie niezależne niezmienne referencje r1 i r2 do s, a na koniec wypisz obie referencje oddzielone spacją za pomocą makra println!.

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

Przypisz `let r1 = &s;` oraz `let r2 = &s;` do pobrania dwóch niezmiennych referencji, a następnie wypisz je za pomocą `println!("{} {}", r1, r2);`.

Rozwiązanie dostępne po 3 próbach