Когда переменная передаётся в функцию, она может быть либо передана по ссылке (borrow, с помощью & или &mut), либо перемещена (move, без ссылки).
Borrow: передаётся ссылка на данные. Данные остаются доступны после вызова функции, но для неизменяемой ссылки нельзя менять содержимое, для изменяемой — может быть только 1 активная ссылка.
fn read_length(s: &String) -> usize { s.len() }
Move: переменная целиком "переезжает" в функцию. После передачи вы не сможете использовать исходную переменную — она была перемещена, и любая попытка обращения приведёт к ошибке компиляции.
fn destroy(s: String) { println!("{}", s); } // s будет уничтожена при выходе let s = String::from("world"); destroy(s); // s больше нельзя использовать
Это предотвращает появление двойного освобождения памяти и других ошибок владения.
Могу ли я использовать переменную после того, как она была передана в функцию по значению (move)?
Нет! После передачи переменной по значению — например, String — исходная переменная становится недействительной:
let s = String::from("abc"); consume(s); // s больше не валидна здесь println!("{}", s); // ошибка компиляции
Многие путают это с поведением типов с Copy (например, i32), где переменная остаётся валидной после передачи.
История
Молодой программист написал функцию обработки строки, которая принимала String, а не &String. В итоге исходная строка после вызова функции становилась недоступной, что привело к двойной загрузке и дополнительному выделению памяти в сервере логирования.
История
В одном микросервисе критичный ресурс передавался в разные потоки через move, после чего попытки доступа к этому ресурсу в основном потоке приводили к ошибке компиляции. Пришлось срочно рефакторить архитектуру, чтобы ресурс передавался по ссылке или через обёртки типа Arc.
История
Во внутреннем парсере событий при обработке данных переменная оперативно забиралась (move) в замыкание, после чего обращения к ней вне closure вызывали компиляционные ошибки. Проблему заметили только при ревью — был введён стиль обязательного использования borrow для данных, которые должны жить дольше локальной функции.