Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Змінні та змінність (Variables and Mutability)

Як згадувалося в розділі «Зберігання значень зі змінними», за замовчуванням змінні є незмінними. Це — один із багатьох поштовхів, які Rust дає вам, щоб писати свій код так, щоб він використовував переваги безпеки та легкої конкурентності, які пропонує Rust. Однак у вас усе ще є можливість зробити ваші змінні змінними. Дослідимо, як і чому Rust заохочує вас надавати перевагу незмінності та чому інколи ви можете захотіти від цього відмовитися.

Коли змінна є незмінною, після того як значення прив’язано до імені, ви не можете змінити це значення. Щоб проілюструвати це, створіть новий проєкт під назвою variables у вашому каталозі projects, використавши cargo new variables.

Потім у вашому новому каталозі variables відкрийте src/main.rs і замініть його код таким кодом, який поки що не скомпілюється:

Filename: src/main.rs

fn main() {
    let x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}

Збережіть і запустіть програму за допомогою cargo run. Ви маєте отримати повідомлення про помилку, пов’язану з незмінністю, як показано в цьому виводі:

$ cargo run
   Compiling variables v0.1.0 (file:///projects/variables)
error[E0384]: cannot assign twice to immutable variable `x`
 --> src/main.rs:4:5
  |
2 |     let x = 5;
  |         - first assignment to `x`
3 |     println!("The value of x is: {x}");
4 |     x = 6;
  |     ^^^^^ cannot assign twice to immutable variable
  |
help: consider making this binding mutable
  |
2 |     let mut x = 5;
  |         +++

For more information about this error, try `rustc --explain E0384`.
error: could not compile `variables` (bin "variables") due to 1 previous error

Цей приклад показує, як компілятор допомагає вам знаходити помилки у ваших програмах. Помилки компілятора можуть дратувати, але насправді вони лише означають, що ваша програма ще не безпечно робить те, що ви хочете, щоб вона робила; вони не означають, що ви не є хорошим програмістом! Досвідчені растацеанці (Rustaceans) досі отримують помилки компілятора.

Ви отримали повідомлення про помилку cannot assign twice to immutable variable `x`, тому що спробували присвоїти друге значення незмінній змінній x.

Важливо, щоб ми отримували помилки часу компіляції, коли намагаємося змінити значення, позначене як незмінне, тому що саме така ситуація може призвести до помилок. Якщо одна частина нашого коду працює, виходячи з припущення, що значення ніколи не зміниться, а інша частина нашого коду змінює це значення, то можливо, що перша частина коду не зробить того, для чого її було спроєктовано. Причину такої помилки може бути важко відстежити згодом, особливо коли другий фрагмент коду змінює значення лише інколи. Компілятор Rust гарантує, що коли ви заявляєте, що значення не зміниться, воно справді не зміниться, тож вам не потрібно відстежувати це самостійно. Таким чином, ваш код легше зрозуміти.

Але змінність може бути дуже корисною і може зробити код зручнішим для написання. Хоча змінні за замовчуванням є незмінними, ви можете зробити їх змінними, додавши mut перед іменем змінної, як ви зробили в розділі 2. Додавання mut також передає намір майбутнім читачам коду, вказуючи, що інші частини коду змінюватимуть значення цієї змінної.

Наприклад, змінимо src/main.rs на таке:

Filename: src/main.rs

fn main() {
    let mut x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
}

Коли ми запускаємо програму тепер, отримуємо таке:

$ cargo run
   Compiling variables v0.1.0 (file:///projects/variables)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.30s
     Running `target/debug/variables`
The value of x is: 5
The value of x is: 6

Нам дозволено змінювати значення, прив’язане до x, з 5 на 6, коли використовується mut. Зрештою, вирішувати, використовувати змінність чи ні, — це залежить від вас і від того, що ви вважаєте найчіткішим у конкретній ситуації.

Оголошення констант (Declaring Constants)

Як і незмінні змінні, константи — це значення, які прив’язуються до імені і не можуть змінюватися, але між константами та змінними є кілька відмінностей.

По-перше, вам не дозволено використовувати mut із константами. Константи не просто незмінні за замовчуванням — вони завжди незмінні. Ви оголошуєте константи, використовуючи ключове слово const замість ключового слова let, і тип значення має бути вказаний. Ми розглянемо типи та анотації типів у наступному розділі, «Типи даних», тож не хвилюйтеся про деталі зараз. Просто знайте, що ви завжди повинні вказувати тип.

Константи можуть бути оголошені в будь-якій області видимості, включно з глобальною областю видимості, що робить їх корисними для значень, про які потрібно знати багатьом частинам коду.

Остання відмінність полягає в тому, що константам можна надавати лише константний вираз, а не результат значення, який можна обчислити лише під час виконання.

Ось приклад оголошення константи:

#![allow(unused)]
fn main() {
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
}

Ім’я константи — THREE_HOURS_IN_SECONDS, а її значення встановлюється як результат множення 60 (кількості секунд у хвилині) на 60 (кількість хвилин у годині) на 3 (кількість годин, які ми хочемо порахувати в цій програмі). Умовність найменування констант у Rust полягає у використанні всіх великих літер із підкресленнями між словами. Компілятор здатний обчислювати обмежений набір операцій під час компіляції, що дає нам змогу записати це значення так, щоб його було легше зрозуміти й перевірити, замість того щоб встановлювати цю константу в значення 10,800. Дивіться розділ Rust Reference про обчислення констант для отримання додаткової інформації про те, які операції можна використовувати під час оголошення констант.

Константи дійсні протягом усього часу виконання програми в межах області видимості, в якій вони були оголошені. Ця властивість робить константи корисними для значень у вашій предметній області застосунку, про які можуть потрібно знати кілька частин програми, наприклад максимальну кількість балів, яку будь-який гравець у грі може набрати, або швидкість світла.

Надання жорстко закодованим значенням, що використовуються у вашій програмі, імен як констант корисне для передавання значення цього значення майбутнім підтримувачам коду. Також зручно мати лише одне місце у вашому коді, яке вам потрібно буде змінити, якщо жорстко закодоване значення потрібно буде оновити в майбутньому.

Затінення (Shadowing)

Як ви бачили в підручнику з гри у вгадування в розділі 2, ви можете оголосити нову змінну з тим самим іменем, що й попередня змінна. растацеанці (Rustaceans) кажуть, що перша змінна затінена другою, що означає, що друга змінна — це те, що компілятор бачитиме, коли ви використовуєте ім’я змінної. Фактично, друга змінна затінює першу, забираючи всі звернення до імені змінної собі, доки або вона сама не буде затінена, або не завершиться область видимості. Ми можемо затінити змінну, використавши те саме ім’я змінної та повторно використавши ключове слово let, як показано нижче:

Filename: src/main.rs

fn main() {
    let x = 5;

    let x = x + 1;

    {
        let x = x * 2;
        println!("The value of x in the inner scope is: {x}");
    }

    println!("The value of x is: {x}");
}

Ця програма спочатку прив’язує x до значення 5. Потім вона створює нову змінну x, повторно записуючи let x =, беручи початкове значення та додаючи 1, так що значення x стає 6. Потім, у внутрішній області видимості, створеній фігурними дужками, третій оператор let також затінює x і створює нову змінну, множачи попереднє значення на 2, щоб надати x значення 12. Коли ця область видимості завершується, внутрішнє затінення закінчується, і x повертається до значення 6. Коли ми запускаємо цю програму, вона виведе таке:

$ cargo run
   Compiling variables v0.1.0 (file:///projects/variables)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31s
     Running `target/debug/variables`
The value of x in the inner scope is: 12
The value of x is: 6

Затінення відрізняється від позначення змінної як mut, тому що ми отримаємо помилку часу компіляції, якщо випадково спробуємо повторно присвоїти цій змінній значення без використання ключового слова let. Використовуючи let, ми можемо виконати кілька перетворень над значенням, але після завершення цих перетворень змінна буде незмінною.

Інша відмінність між mut і затіненням полягає в тому, що оскільки ми фактично створюємо нову змінну, коли знову використовуємо ключове слово let, ми можемо змінити тип значення, але повторно використати те саме ім’я. Наприклад, припустімо, що наша програма просить користувача показати, скільки пробілів він хоче між якимось текстом, вводячи символи пробілу, а потім ми хочемо зберегти це введення як число:

fn main() {
    let spaces = "   ";
    let spaces = spaces.len();
}

Перша змінна spaces має тип рядка, а друга змінна spaces має числовий тип. Отже, затінення позбавляє нас необхідності вигадувати різні імена, такі як spaces_str і spaces_num; натомість ми можемо повторно використати простішу назву spaces. Однак, якщо ми спробуємо використати для цього mut, як показано тут, ми отримаємо помилку часу компіляції:

fn main() {
    let mut spaces = "   ";
    spaces = spaces.len();
}

У повідомленні про помилку сказано, що нам не дозволено змінювати тип змінної:

$ cargo run
   Compiling variables v0.1.0 (file:///projects/variables)
error[E0308]: mismatched types
 --> src/main.rs:3:14
  |
2 |     let mut spaces = "   ";
  |                      ----- expected due to this value
3 |     spaces = spaces.len();
  |              ^^^^^^^^^^^^ expected `&str`, found `usize`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `variables` (bin "variables") due to 1 previous error

Тепер, коли ми дослідили, як працюють змінні, давайте подивимося на інші типи даних, які вони можуть мати.