Podczas przekazywania zmiennej do funkcji, może być ona przekazana przez odniesienie (borrow, za pomocą & lub &mut), lub przeniesiona (move, bez odniesienia).
Borrow: przekazywane jest odniesienie do danych. Dane pozostają dostępne po wywołaniu funkcji, ale dla niezmiennego odniesienia nie można zmieniać zawartości, dla zmiennego — może być tylko 1 aktywne odniesienie.
fn read_length(s: &String) -> usize { s.len() }
Move: zmienna całkowicie "przeprowadza się" do funkcji. Po przekazaniu nie będziesz mógł używać oryginalnej zmiennej — została przeniesiona, a wszelkie próby dostępu spowodują błąd kompilacji.
fn destroy(s: String) { println!("{}", s); } // s zostanie zniszczona przy wyjściu let s = String::from("world"); destroy(s); // s już nie można używać
Zapobiega to podwójnemu zwolnieniu pamięci i innym błędom związanym z własnością.
Czy mogę używać zmiennej po tym, jak została przekazana do funkcji przez wartość (move)?
Nie! Po przekazaniu zmiennej przez wartość — na przykład String — oryginalna zmienna staje się nieważna:
let s = String::from("abc"); consume(s); // s jest nieważna tutaj println!("{}", s); // błąd kompilacji
Wielu myli to z zachowaniem typów z Copy (na przykład i32), gdzie zmienna pozostaje ważna po przekazaniu.
Historia
Młody programista napisał funkcję do przetwarzania łańcucha, która przyjmowała String, a nie &String. W rezultacie oryginalny łańcuch po wywołaniu funkcji stawał się niedostępny, co prowadziło do podwójnego załadowania i dodatkowego przydziału pamięci w serwerze logowania.
Historia
W jednym mikroserwisie krytyczny zasób był przekazywany do różnych wątków przez move, po czym próby uzyskania dostępu do tego zasobu w wątku głównym prowadziły do błędu kompilacji. Trzeba było pilnie refaktoryzować architekturę, aby zasób był przekazywany przez odniesienie lub przez opakowania typu Arc.
Historia
W wewnętrznym parserze zdarzeń podczas przetwarzania danych zmienna była szybko zabierana (move) do zamknięcia, po czym odwołania do niej poza closure powodowały błędy kompilacji. Problem zauważono dopiero podczas przeglądu — wprowadzono styl obowiązkowego użycia borrow dla danych, które powinny żyć dłużej niż lokalna funkcja.