# Rust: Основи для досвідчених розробників у 2026 році > Швидке освоєння Rust з використанням наявного досвіду програмування. Власність, запозичення, часи життя та ключові патерни для розробників, які переходять з C++, Java або Python. - Published: 2026-01-09 - Updated: 2026-04-01 - Author: Anthony Fillion-Maillet - Tags: rust, systems programming, ownership, memory safety, performance - Reading time: 14 min --- Rust продовжує набирати популярність рік за роком, і на це є вагомі причини: гарантована безпека пам'яті на етапі компіляції, продуктивність рівня C++ та сучасна екосистема. Для досвідчених розробників, які приходять з C++, Java або Python, початок роботи з Rust може здатися незвичним, але фундаментальні концепції швидко стають інтуїтивними після їх засвоєння. > **Чому Rust у 2026 році?** > > Rust утримує звання найулюбленішої мови на Stack Overflow вже 8 років поспіль. Прийнятий Microsoft, Google, Amazon та Meta для критично важливих компонентів, Rust забезпечує безпеку пам'яті без збирача сміття. ## Налаштування середовища розробки Rust Перед написанням коду необхідно встановити Rust через rustup -- офіційний інструмент керування версіями. ```bash # install.sh # Install Rust via rustup (macOS, Linux) curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Verify installation rustc --version cargo --version ``` Cargo -- це пакетний менеджер та інструмент збірки Rust. Він поєднує функціональність npm, Maven та Make в одному узгодженому інструменті. ```bash # project-setup.sh # Create a new project cargo new my_project cd my_project # Generated structure: # my_project/ # ├── Cargo.toml # Manifest (like package.json) # └── src/ # └── main.rs # Entry point # Essential commands cargo build # Compile the project cargo run # Compile and run cargo test # Run tests cargo check # Check without building (faster) ``` ## Змінні та незмінність за замовчуванням Rust змінює звичну конвенцію: змінні є **незмінними за замовчуванням**. Такий підхід змушує явно продумувати мутабельність та запобігає багатьом помилкам. ```rust // variables.rs fn main() { // Immutable by default let x = 5; // x = 6; // Compile error! // Explicitly mutable variable let mut y = 5; y = 6; // OK // Shadowing: redeclaration in the same scope let x = x + 1; // Creates a new variable x let x = x * 2; // x is now 12 // Shadowing also allows changing the type let spaces = " "; // &str let spaces = spaces.len(); // usize } ``` > **Затінення (Shadowing) та мутабельність** > > Затінення створює нову змінну, на відміну від `mut`, яке змінює існуюче значення. Затінення дозволяє трансформувати значення, зберігаючи зрозуміле ім'я. ## Фундаментальні типи даних Rust -- статично типізована мова з чудовим виведенням типів. Нижче наведено основні примітивні типи, які необхідно знати. ```rust // types.rs fn main() { // Signed integers: i8, i16, i32, i64, i128, isize let age: i32 = 30; // Unsigned integers: u8, u16, u32, u64, u128, usize let count: u64 = 1_000_000; // Underscores for readability // Floats: f32, f64 (default) let pi: f64 = 3.14159; // Boolean let active: bool = true; // Unicode character (4 bytes) let emoji: char = '🦀'; // Tuple: fixed collection of different types let person: (String, i32) = (String::from("Alice"), 28); let (name, age) = person; // Destructuring // Array: fixed size, same type let numbers: [i32; 5] = [1, 2, 3, 4, 5]; let first = numbers[0]; // Slice: view into a portion of data let slice: &[i32] = &numbers[1..4]; // [2, 3, 4] } ``` ## Власність (Ownership): Революційна концепція Власність -- це головна інновація Rust. Ця система гарантує безпеку пам'яті без збирача сміття, ціною початкової кривої навчання. ### Три правила власності ```rust // ownership.rs fn main() { // Rule 1: Each value has a single owner let s1 = String::from("hello"); // Rule 2: When the owner goes out of scope, the value is freed { let s2 = String::from("world"); // s2 is valid here } // s2 is dropped, no longer accessible // Rule 3: Only one ownership at a time (move) let s3 = s1; // s1 is MOVED to s3 // println!("{}", s1); // Error: s1 is no longer valid! println!("{}", s3); // OK: s3 is the owner } ``` ### Переміщення (Move) та клонування (Clone) ```rust // move_clone.rs fn main() { // Simple types (stack): automatic Copy let x = 5; let y = x; // Copy, not move println!("x = {}, y = {}", x, y); // Both are valid // Complex types (heap): Move by default let s1 = String::from("hello"); let s2 = s1; // Move // s1 is no longer usable // Explicit clone to duplicate let s3 = String::from("world"); let s4 = s3.clone(); // Deep copy println!("s3 = {}, s4 = {}", s3, s4); // Both valid } ``` > **Переміщення та функції** > > Передача значення у функцію переносить власність. Функція стає новим власником, і значення стає недоступним після виклику, якщо його не повернено. ```rust // ownership_functions.rs fn main() { let s = String::from("hello"); takes_ownership(s); // println!("{}", s); // Error: s has been moved let x = 5; makes_copy(x); println!("{}", x); // OK: i32 implements Copy // To regain ownership, return the value let s2 = String::from("hello"); let s3 = takes_and_gives_back(s2); println!("{}", s3); // OK } fn takes_ownership(s: String) { println!("{}", s); } // s is dropped here fn makes_copy(x: i32) { println!("{}", x); } fn takes_and_gives_back(s: String) -> String { s // Returns ownership } ``` ## Запозичення (Borrowing): Посилання без передачі власності Запозичення дозволяє використовувати значення без отримання власності. Це найчастіше використовуваний механізм у Rust. ```rust // borrowing.rs fn main() { let s1 = String::from("hello"); // Immutable reference: read-only let len = calculate_length(&s1); println!("Length of '{}': {}", s1, len); // s1 still valid // Mutable reference: modification allowed let mut s2 = String::from("hello"); change(&mut s2); println!("{}", s2); // "hello, world" } fn calculate_length(s: &String) -> usize { s.len() } // s goes out of scope but doesn't drop (it's a reference) fn change(s: &mut String) { s.push_str(", world"); } ``` ### Правила запозичення ```rust // borrowing_rules.rs fn main() { let mut s = String::from("hello"); // Rule 1: Multiple immutable references simultaneously OK let r1 = &s; let r2 = &s; println!("{} and {}", r1, r2); // Rule 2: ONLY ONE mutable reference at a time let r3 = &mut s; // let r4 = &mut s; // Error: already borrowed mutably println!("{}", r3); // Rule 3: No mutable ref if immutable ref exists let r5 = &s; // let r6 = &mut s; // Error: r5 is still active println!("{}", r5); // Once r5 is used for the last time, mutable borrow is allowed let r7 = &mut s; // OK: r5 is no longer used after this r7.push_str("!"); } ``` ## Часи життя (Lifetimes): Гарантія валідності посилань Часи життя забезпечують збереження валідності посилань. Компілятор часто виводить їх автоматично, але іноді потрібна явна анотація. ```rust // lifetimes_basic.rs // Classic error: reference to freed data // fn dangling() -> &String { // let s = String::from("hello"); // &s // Error: s will be dropped, invalid reference! // } // Solution: return the owned value fn no_dangle() -> String { let s = String::from("hello"); s // Ownership transferred, no problem } ``` ### Анотації часів життя ```rust // lifetimes_annotation.rs // The compiler can't figure out which reference will be returned // fn longest(x: &str, y: &str) -> &str { ... } // Error! // Explicit annotation: return lives as long as BOTH x AND y fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let string1 = String::from("long string"); let result; { let string2 = String::from("xyz"); result = longest(&string1, &string2); println!("Longest: {}", result); // OK here } // println!("{}", result); // Error: string2 dropped } ``` > **Правила елізії часів життя** > > Rust застосовує правила елізії, що позбавляють необхідності анотування простих випадків. На початковому етапі достатньо слідувати явним повідомленням компілятора. ## Структури (Structs) та реалізації (Implementations) Структури -- це базові будівельні блоки для створення власних типів у Rust. ```rust // structs.rs // Struct definition #[derive(Debug)] // Enables printing with {:?} struct User { username: String, email: String, active: bool, sign_in_count: u64, } // Implementation block for methods impl User { // Constructor (convention: fn new or descriptive name) fn new(username: String, email: String) -> Self { Self { username, email, active: true, sign_in_count: 1, } } // Method: takes &self (reference to instance) fn is_active(&self) -> bool { self.active } // Method with mutation: takes &mut self fn deactivate(&mut self) { self.active = false; } // Method consuming self (rare) fn into_username(self) -> String { self.username } } fn main() { let mut user = User::new( String::from("alice"), String::from("alice@example.com"), ); println!("Active: {}", user.is_active()); user.deactivate(); println!("Active: {}", user.is_active()); // Debug print println!("{:?}", user); } ``` ## Перерахування (Enums) та зіставлення зі зразком (Pattern Matching) Перерахування в Rust значно потужніші за аналоги в більшості мов: кожен варіант може містити дані. ```rust // enums.rs // Simple enum enum Direction { North, South, East, West, } // Enum with data (algebraic data type) enum Message { Quit, Move { x: i32, y: i32 }, Write(String), ChangeColor(u8, u8, u8), } impl Message { fn process(&self) { match self { Message::Quit => println!("Quitting"), Message::Move { x, y } => println!("Moving to ({}, {})", x, y), Message::Write(text) => println!("Writing: {}", text), Message::ChangeColor(r, g, b) => { println!("Color: rgb({}, {}, {})", r, g, b) } } } } fn main() { let msg = Message::Move { x: 10, y: 20 }; msg.process(); let msg2 = Message::Write(String::from("Hello Rust!")); msg2.process(); } ``` ### Option та Result: Основи обробки помилок ```rust // option_result.rs use std::fs::File; use std::io::{self, Read}; fn main() { // Option: present or absent value (replaces null) let numbers = vec![1, 2, 3]; let first: Option<&i32> = numbers.first(); match first { Some(n) => println!("First: {}", n), None => println!("Empty list"), } // Utility methods let value = first.unwrap_or(&0); let doubled = first.map(|n| n * 2); // Result: success or error let result = read_file("config.txt"); match result { Ok(content) => println!("Content: {}", content), Err(e) => println!("Error: {}", e), } } fn read_file(path: &str) -> Result { let mut file = File::open(path)?; // ? propagates the error let mut content = String::new(); file.read_to_string(&mut content)?; Ok(content) } ``` > **Оператор ?** > > Оператор `?` -- це синтаксичний цукор для поширення помилок. Він автоматично повертає помилку, якщо Result є Err, інакше розгортає значення Ok. ## Трейти (Traits): Поліморфізм у стилі Rust Трейти визначають спільну поведінку, подібно до інтерфейсів у Java або протоколів у Swift. ```rust // traits.rs // Trait definition trait Summary { fn summarize(&self) -> String; // Method with default implementation fn preview(&self) -> String { format!("{}...", &self.summarize()[..50.min(self.summarize().len())]) } } struct Article { title: String, author: String, content: String, } struct Tweet { username: String, content: String, } // Implementation for Article impl Summary for Article { fn summarize(&self) -> String { format!("{} by {}", self.title, self.author) } } // Implementation for Tweet impl Summary for Tweet { fn summarize(&self) -> String { format!("@{}: {}", self.username, self.content) } } // Function accepting any type implementing Summary fn notify(item: &impl Summary) { println!("Breaking news: {}", item.summarize()); } // Equivalent syntax with trait bound fn notify_generic(item: &T) { println!("Breaking news: {}", item.summarize()); } fn main() { let article = Article { title: String::from("Rust 2026"), author: String::from("Community"), content: String::from("..."), }; let tweet = Tweet { username: String::from("rustlang"), content: String::from("Rust is awesome!"), }; notify(&article); notify(&tweet); } ``` ## Основні колекції Стандартна бібліотека Rust надає потужні типи колекцій. ```rust // collections.rs use std::collections::HashMap; fn main() { // Vec: dynamic array let mut numbers: Vec = Vec::new(); numbers.push(1); numbers.push(2); numbers.push(3); // vec! macro for initialization let nums = vec![1, 2, 3, 4, 5]; // Iteration for n in &nums { println!("{}", n); } // Functional methods let doubled: Vec = nums.iter().map(|x| x * 2).collect(); let sum: i32 = nums.iter().sum(); let evens: Vec<&i32> = nums.iter().filter(|x| *x % 2 == 0).collect(); // String: growable UTF-8 string let mut s = String::from("Hello"); s.push_str(", World!"); s.push('!'); // Concatenation let s1 = String::from("Hello, "); let s2 = String::from("World!"); let s3 = s1 + &s2; // s1 moved, s2 borrowed // or with format! let s4 = format!("{}{}", "Hello, ", "World!"); // HashMap let mut scores: HashMap = HashMap::new(); scores.insert(String::from("Blue"), 10); scores.insert(String::from("Red"), 50); // Access with get (returns Option) if let Some(score) = scores.get("Blue") { println!("Blue: {}", score); } // Entry API for conditional insertion scores.entry(String::from("Yellow")).or_insert(25); } ``` ## Ідіоматична обробка помилок у Rust Правильна обробка помилок має критичне значення в Rust. Нижче наведено рекомендовані патерни. ```rust // error_handling.rs use std::fs::File; use std::io::{self, Read}; use std::num::ParseIntError; // Define a custom error type #[derive(Debug)] enum AppError { Io(io::Error), Parse(ParseIntError), Custom(String), } // Implement From for automatic conversion impl From for AppError { fn from(err: io::Error) -> Self { AppError::Io(err) } } impl From for AppError { fn from(err: ParseIntError) -> Self { AppError::Parse(err) } } // Function returning Result with custom error fn read_number_from_file(path: &str) -> Result { let mut file = File::open(path)?; // io::Error -> AppError let mut content = String::new(); file.read_to_string(&mut content)?; let number: i32 = content.trim().parse()?; // ParseIntError -> AppError Ok(number) } fn main() { match read_number_from_file("number.txt") { Ok(n) => println!("Number: {}", n), Err(AppError::Io(e)) => println!("IO error: {}", e), Err(AppError::Parse(e)) => println!("Parse error: {}", e), Err(AppError::Custom(msg)) => println!("Error: {}", msg), } } ``` > **Рекомендовані крейти** > > Для реальних проєктів крейт `thiserror` (для бібліотек) та `anyhow` (для застосунків) значно спрощують обробку помилок. ## Тестування в Rust Rust інтегрує фреймворк тестування безпосередньо в мову. ```rust // lib.rs pub fn add(a: i32, b: i32) -> i32 { a + b } pub fn divide(a: i32, b: i32) -> Result { if b == 0 { Err(String::from("Division by zero")) } else { Ok(a / b) } } // Test module (compiled only for `cargo test`) #[cfg(test)] mod tests { use super::*; #[test] fn test_add() { assert_eq!(add(2, 3), 5); } #[test] fn test_add_negative() { assert_eq!(add(-1, 1), 0); } #[test] fn test_divide_success() { assert_eq!(divide(10, 2), Ok(5)); } #[test] fn test_divide_by_zero() { assert!(divide(10, 0).is_err()); } #[test] #[should_panic(expected = "index out of bounds")] fn test_panic() { let v = vec![1, 2, 3]; let _ = v[99]; // Panic! } } ``` ## Висновок Rust пропонує унікальну парадигму, що поєднує безпеку пам'яті та продуктивність. Концепції власності та запозичення спочатку здаються обмежувальними, але з практикою стають природними. Компілятор Rust -- безцінний помічник: його повідомлення про помилки вважаються одними з найкращих у галузі. ### Контрольний список для початку - Встановлення Rust через rustup та опанування Cargo - Розуміння різниці між незмінністю та явною мутабельністю - Засвоєння трьох правил власності - Практика запозичення з посиланнями `&` та `&mut` - Використання `Option` та `Result` замість null та виключень - Написання тестів за допомогою `#[test]` Спільнота Rust привітна, а ресурсів достатньо. Офіційна книга "The Rust Programming Language" доступна безкоштовно онлайн. Маючи ці надійні основи, можна переходити до поглиблених тем: async/await, макроси та WebAssembly. --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/uk/blog/rust/rust-basics-experienced-developers