Borrowing (pożyczanie) w Rust to mechanizm tymczasowego "pożyczania" zmiennej za pomocą referencji. Rust rozróżnia niemutowalne pożyczanie (&T) i mutowalne (&mut T). Jednocześnie może istnieć dowolna liczba niemutowalnych referencji, ale tylko jedna mutowalna. Nie można jednocześnie mieć mutowalnej i niemutowalnej referencji do tego samego obiektu.
Ta zasada gwarantuje brak warunków wyścigu (data race) na etapie kompilacji i sprawia, że Rust jest bezpieczny dla programowania współbieżnego.
Przykład:
let mut value = 5; let r1 = &value; let r2 = &value; // let r3 = &mut value; // BŁĄD: nie można tworzyć &mut, gdy istnieje & println!("{} {}", r1, r2); // r3 jest zabroniony do końca obszaru widoczności r1/r2
Pytanie: Czy można w jednym obszarze widoczności stworzyć jedną referencję &mut i dowolną liczbę referencji & do tego samego obiektu?
Typowa błędna odpowiedź: Tak, ale tylko jeśli nie nakładają się na siebie w zakresie życia.
Poprawna odpowiedź: Nie może jednocześnie istnieć mutowalna i niemutowalna referencja do tego samego obiektu (nawet jeśli ich użycia nie nakładają się statycznie w kodzie), dopóki jedna z nich żyje — inne są zabronione. Bezpieczeństwo sprawdza borrow checker.
Przykład:
let mut x = 10; let y = &x; let z = &mut x; // Błąd, y wciąż jest w obszarze widoczności println!("{}", y); // y potrzebne później
Historia
W dużym projekcie z równoległym przetwarzaniem danych programista postanowił używać niemutowalnych referencji do wektora, a następnie próbował uzyskać mutowalną do sortowania. Kod działał w teście, ale przestał się kompilować po refaktoryzacji, ponieważ wydłużył się czas życia niemutowalnych referencji.
Historia
W wewnętrznej usłudze zmieniano strukturę przez &mut, zachowując przy tym referencje do pól do późniejszego przesłania do innego wątku. Powstał wyścig danych i awaria z powodu nieprzestrzegania zasad borrow — Rust chroni przed tym tylko na etapie kompilacji, ale błędy wystąpiły w blokach unsafe, w których gwarancje zostały zniesione.
Historia
Niepoprawna dokumentacja API: biblioteka przyjmowała jednocześnie & i &mut do różnych pól struktury, ale z powodu aliasingu naruszano inwariantność, co prowadziło do trudnych do uchwycenia błędów w usługach integrujących tę bibliotekę.