Borrowing, slices
VTFK · 2026
Фокус: позичити — не означає володіти
Позичити — не означає володіти: посилання нічого не звільняє при виході з області видимості
fn calculate_length(s: &String) -> usize {
s.len()
} // s виходить з області видимості, але дані НЕ звільняються — s лише позичав
let s1 = String::from("привіт");
let len = calculate_length(&s1);
println!("{}: {}", s1, len); // s1 і далі валідний — власність не передавалась& — читати, не володіти
Data race = два потоки звертаються до тих самих даних одночасно, і хоча б один запис
| Уже є | Додати ще &T | Додати &mut T |
|---|---|---|
| нічого | ✓ | ✓ |
&T (одне чи багато) | ✓ | ✗ error[E0502] |
&mut T | ✗ error[E0502] | ✗ error[E0499] |
Це класична задача «читачі–письменник», розв'язана не блокуванням у рантаймі, а відмовою компілювати
let mut s = String::from("привіт");
let r1 = &s; // незмінне запозичення
let r2 = &mut s; // error[E0502]: cannot borrow `s` as mutable
// because it is also borrowed as immutable
println!("{} {}", r1, r2);error[E0502]: конфлікт запозичень виявлено до запуску програми
Очікувано: Перша версія: помилка компіляції з точним рядком конфлікту. Друга: успішна збірка
| Власник | Зріз | Що містить зріз | Приклад |
|---|---|---|---|
String | &str | вказівник на початок + довжина | "літерал", s.as_str(), &s[..i] |
Vec<T> | &[T] | вказівник + довжина | &v[1..3] |
[T; N] | &[T] | вказівник + довжина | &arr[..] |
Зріз — посилання на неперервну ділянку колекції: не володіє даними, нічого не копіює і не звільняє
`&v[2..5]` — це вказівник на v[2] плюс довжина 3. Поки цей зріз живий, `v.push(60)` потребує `&mut v` і не компілюється: error[E0502]
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &b) in bytes.iter().enumerate() {
if b == b' ' { return &s[..i]; }
}
s
}
let v = vec![10, 20, 30, 40];
let mid = &v[1..3]; // зріз [20, 30], без копіювання данихЗрізи дозволяють працювати з частиною даних без володіння й без копіювання
fn largest(list: &[i32]) -> i32 {
let mut max = list[0];
for &item in list {
if item > max { max = item; }
}
max
}
let numbers = vec![34, 50, 25, 3101, 65];
println!("{}", largest(&numbers)); // 3101, без копіювання вектораФункція приймає зріз — працює і з Vec<i32>, і з масивом [i32; N]
Ціна — явно продумувати, хто й коли позичає дані; це і є «суворість», яку ставлять Rust у докір
NLL аналізує потік керування, а не текстову область видимості змінної
let mut v = vec![1, 2, 3];
let first = &v[0]; // незмінне запозичення починається
println!("{}", first); // останнє використання first — тут
v.push(4); // ОК під NLL: first більше не використовуєтьсяДо появи NLL цей код відмовлявся компілюватись — first текстово "жила" до кінця блоку
Детально — на ЛР5 і в парному СРС: там це інструмент, а не теорія заради теорії
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
let s1 = String::from("довгий рядок");
let s2 = String::from("короткий");
println!("{}", longest(&s1, &s2));Без 'a компілятор не знав би, з якого саме аргументу походить повернене посилання
Саме правило 3 дозволяє писати fn get(&self) -> &str без 'a
let mut v = vec![1, 2, 3];
for x in &v {
v.push(*x); // error[E0502]: cannot borrow `v` as mutable
// because it is also borrowed as immutable
}Iterator invalidation — класичний рантайм-баг в інших мовах — тут помилка компіляції
| Пастка | Що станеться | Як уникнути |
|---|---|---|
&mut і & до однієї змінної в одній ділянці | error[E0502] | розвести запозичення в часі; часто рятує NLL |
Повернути &str на локальну String | висяче посилання — не компілюється | повертати String або приймати вхідне посилання |
v.push() усередині for x in &v | error[E0502]: інвалідація ітератора | зібрати зміни окремо, застосувати після циклу |
Вважати, що 'a «створює» час життя | контракт не збігається з реальністю | 'a лише описує наявну область життя даних |
У будь-який момент часу можна мати або одне змінне посилання, або будь-яку кількість незмінних — і посилання завжди мають бути дійсними.
— The Rust Programming Language, розд. 4.2 «References and Borrowing»
🥚 Найбільше число у прикладі largest — 3101, тобто 0xC1D. Функція повертає його копією, не забираючи володіння вектором; сама константа супроводжує курс від першого модуля.