# Ownership i Borrowing у Rust: повний посібник з прикладами коду > Детальний розбір ownership та borrowing у Rust з практичними прикладами. Семантика переміщення, посилання, часи життя та borrow checker для безпечного керування пам'яттю. - Published: 2026-03-31 - Updated: 2026-03-31 - Author: Anthony Fillion-Maillet - Tags: rust, ownership, borrowing, memory-management, systems-programming - Reading time: 10 min --- Ownership та borrowing становлять основу гарантій безпеки пам'яті в Rust. На відміну від мов зі збиранням сміття, Rust застосовує суворі правила на етапі компіляції через borrow checker, що усуває цілі класи помилок -- розіменування нульових вказівників, гонки даних та використання після звільнення -- без жодних витрат під час виконання. > **Три правила ownership** > > Кожне значення в Rust має рівно одного власника. Коли власник виходить з області видимості, значення знищується. Право власності можна передати (перемістити) або тимчасово надати в користування (позичити). Ці три правила повністю замінюють збирання сміття. ## Як семантика переміщення замінює збирання сміття Більшість мов програмування дозволяють кільком змінним вказувати на одні й ті самі дані в купі. Rust обирає інший підхід: присвоєння значення з купи іншій змінній *переміщує* його, роблячи оригінальну прив'язку недійсною. Компілятор гарантує це без будь-яких витрат. ```rust // move_semantics.rs fn main() { let original = String::from("interview prep"); let moved = original; // ownership transfers here // println!("{}", original); // compile error: value moved println!("{}", moved); // works fine } ``` Такий підхід запобігає помилкам подвійного звільнення. Тип `String` виділяє пам'ять у купі, тому Rust гарантує, що лише одна змінна володіє цим виділенням у будь-який момент часу. Типи, що зберігаються виключно на стеку, такі як `i32` або `bool`, реалізують трейт `Copy` і дублюються замість переміщення. ```rust // copy_vs_move.rs fn main() { let x: i32 = 42; let y = x; // copy, not move -- i32 is Copy println!("x = {}, y = {}", x, y); // both valid let s1 = String::from("hello"); let s2 = s1.clone(); // explicit deep copy println!("s1 = {}, s2 = {}", s1, s2); // both valid after clone } ``` Різниця між `Copy` та `Clone` має значення на співбесідах: `Copy` -- неявний і дешевий (побітове копіювання), тоді як `Clone` -- явний і може бути дорогим (виділення пам'яті в купі). ## Запозичення через незмінні посилання Передача права власності скрізь зробила б код непрактичним. Borrowing у Rust вирішує цю проблему, надаючи доступ до значення без передачі права власності. Незмінне посилання (`&T`) дозволяє доступ лише для читання, і кілька незмінних посилань можуть існувати одночасно. ```rust // immutable_borrowing.rs fn calculate_length(s: &String) -> usize { s.len() // read access through the reference } // s goes out of scope, but doesn't drop the String (not the owner) fn main() { let greeting = String::from("hello, Rust"); let len = calculate_length(&greeting); // borrow, don't move println!("'{}' has {} characters", greeting, len); // greeting still valid } ``` Символ `&` створює посилання, яке запозичує значення. Сигнатура функції `&String` оголошує, що `calculate_length` запозичує без отримання права власності. Після повернення функції викликаючий код зберігає повне право власності. > **Правила запозичення** > > У будь-який момент часу значення може мати або: багато незмінних посилань (`&T`), АБО рівно одне змінне посилання (`&mut T`). Ніколи обидва одночасно. Це правило запобігає гонкам даних на етапі компіляції. ## Змінні посилання та правило ексклюзивності Змінні посилання (`&mut T`) надають доступ для запису, але вимагають ексклюзивності: лише одне змінне посилання на значення може існувати в заданій області видимості. Це запобігає одночасній модифікації одних і тих самих даних двома фрагментами коду. ```rust // mutable_borrowing.rs fn append_greeting(s: &mut String) { s.push_str(", welcome to Rust!"); // modify through mutable ref } fn main() { let mut message = String::from("Hello"); append_greeting(&mut message); println!("{}", message); // "Hello, welcome to Rust!" } ``` Ключове слово `mut` з'являється у трьох місцях: прив'язка змінної (`let mut`), тип посилання (`&mut`) та параметр функції. Усі три є обов'язковими. Спроба створити друге змінне посилання в тій самій області видимості спричиняє помилку компіляції. ```rust // exclusivity_rule.rs fn main() { let mut data = String::from("shared state"); let r1 = &mut data; // let r2 = &mut data; // compile error: second mutable borrow println!("{}", r1); // After r1's last usage, a new mutable borrow is allowed let r3 = &mut data; // this works -- non-lexical lifetimes r3.push_str(" updated"); println!("{}", r3); } ``` Редакція Rust 2021 використовує нелексичні часи життя (NLL): запозичення завершується в точці останнього використання, а не в кінці блоку області видимості. Це робить правило ексклюзивності більш ергономічним без втрати безпеки. ## Часи життя: як пояснити компілятору тривалість існування посилань Часи життя (lifetimes) -- це спосіб Rust гарантувати, що посилання ніколи не переживуть дані, на які вони вказують. Здебільшого компілятор виводить часи життя автоматично через *правила виведення часів життя*. Явні анотації стають необхідними, коли взаємодіють кілька посилань. ```rust // lifetime_annotation.rs fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let result; let string1 = String::from("Rust ownership"); { let string2 = String::from("borrowing"); result = longest(string1.as_str(), string2.as_str()); println!("Longest: {}", result); } } ``` Синтаксис `'a` -- це параметр часу життя, а не нова концепція. Він анотує зв'язки, які вже існують. Сигнатура функції стверджує: "вихідне посилання не може пережити жодне з вхідних посилань". Компілятор використовує це для запобігання висячим посиланням. ## Запозичення у структурах та обмеження часів життя Структури, що містять посилання, повинні оголошувати параметри часів життя. Це гарантує, що структура не може пережити дані, на які посилається -- поширене джерело висячих вказівників у C/C++. ```rust // struct_lifetime.rs struct Excerpt<'a> { text: &'a str, } impl<'a> Excerpt<'a> { fn summary(&self) -> &str { let end = self.text.len().min(20); &self.text[..end] } } fn main() { let article = String::from("Rust ownership model eliminates memory bugs"); let excerpt = Excerpt { text: article.as_str(), }; println!("Summary: {}", excerpt.summary()); } ``` Час життя `'a` у `Excerpt<'a>` прив'язує валідність структури до базового рядка. Знищення `article` до `excerpt` спричинить помилку компіляції. > **Типова пастка на співбесіді** > > Питання про висячі посилання часто зустрічаються на [співбесідах з Rust](/technologies/rust/interview-questions/smart-pointers). Відповідь завжди одна: Rust запобігає їм на етапі компіляції через аналіз часів життя. Жодних перевірок під час виконання, жодних нульових вказівників. ## Патерни ownership у реальному коді на Rust Продакшн-код на Rust базується на кількох повторюваних патернах ownership. Розпізнавання цих патернів прискорює як розробку, так і підготовку до співбесід. ```rust // ownership_patterns.rs // Pattern 1: Take ownership, return a new value fn process_and_return(mut input: String) -> String { input.push_str(" -- processed"); input } // Pattern 2: Borrow for read-only inspection fn contains_keyword(text: &str, keyword: &str) -> bool { text.to_lowercase().contains(&keyword.to_lowercase()) } // Pattern 3: Borrow mutably for in-place modification fn sanitize(input: &mut String) { *input = input.trim().to_string(); } fn main() { let raw = String::from("user input"); let processed = process_and_return(raw); let found = contains_keyword(&processed, "input"); println!("Contains 'input': {}", found); let mut padded = String::from(" spaces everywhere "); sanitize(&mut padded); println!("Sanitized: '{}'", padded); } ``` Вибір між цими патернами слідує простій евристиці: за замовчуванням запозичувати незмінно, запозичувати змінно коли потрібна модифікація, і передавати право власності лише тоді, коли викликаючий код більше не потребує значення. Цей підхід детально розглянуто у [посібнику з основ Rust](/blog/rust/rust-basics-experienced-developers). ## Помилки borrow checker та способи їх виправлення Borrow checker генерує специфічні коди помилок. Розуміння найпоширеніших з них перетворює роздратовуючі помилки компіляції на прості виправлення. ```rust // common_fixes.rs fn main() { let mut scores = vec![90, 85, 78]; let first = scores[0]; scores.push(95); println!("First: {}, All: {:?}", first, scores); let name = String::from("Alice"); let greeting = format!("Hello, {}", name); println!("{} says {}", name, greeting); let outer; { let inner = String::from("temporary"); outer = inner; } println!("{}", outer); } ``` Кожне виправлення слідує одному принципу: реструктуризувати код так, щоб запозичення та право власності відповідали правилам Rust. Боротьба з borrow checker зазвичай сигналізує про проблему проектування, яка спричинила б помилки в інших мовах. Для більш складних патернів, пов'язаних з [конкурентністю та запозиченням](/technologies/rust/interview-questions/concurrency-basics), типи спільного володіння на кшталт `Arc` та `Mutex` стають незамінними. ## Висновок - Кожне значення в Rust має одного власника; право власності передається при присвоєнні (семантика переміщення), якщо тип не реалізує `Copy` - Незмінні посилання (`&T`) дозволяють спільний доступ для читання; змінні посилання (`&mut T`) забезпечують ексклюзивний доступ для запису - Borrow checker запобігає гонкам даних та висячим посиланням на етапі компіляції з нульовими витратами під час виконання - Часи життя анотують зв'язки між посиланнями -- вони описують існуючі обмеження, а не створюють нові - Коли borrow checker відхиляє код, слід реструктуризувати потік ownership замість використання `unsafe` - Практика цих патернів з [питаннями для співбесід з Rust](/blog/rust/rust-interview-questions) допоможе набути впевненості перед технічними співбесідами --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/uk/blog/rust/rust-ownership-borrowing-demystified