Небагато мов так високо цінуються розробниками, як Rust, яка обіцяє надійну безпеку пам’яті без шкоди для швидкості. Проте Rust має круту криву навчання, а деякі із її концепцій програмування можуть здатися перешкодою.
На допомогу початківцям у Rust InfoWorld пропонує чотири ключові концепції, які більш досвідчений розробник Rust хотів би знати із самого початку. Розуміння цих концепцій дасть вам перевагу, коли ви почнете писати програми на Rust.
Усі змінні Rust незмінні за замовчуванням
Будь-яка змінна, яку ви створюєте у Rust, незмінна за замовчуванням.
Якщо ви напишетеlet x=1;
, зміннаx
буде прив’язана до значення1
протягом усього життя програми. Будь-яка змінна, яка може змінитися, має бути спеціально оголошена як така за допомогою ключового слова mut
(для «mutable»). Наприклад, let mut x=1;
.
Ця стратегія є свідомим вибором дизайну для Rust, хоча спочатку це може здатися жахливим. Але ось обґрунтування: про незмінні сутності у програмі легше міркувати та давати гарантії, особливо гарантії щодо безпеки пам’яті, що є головною перевагою Rust.
Якщо компілятор знає, що якийсь елемент у цій області не змінюється після його призначення, більшість проблем керування пам’яттю навколо цього елемента зникає.
Інше обґрунтування цього дизайнерського рішення полягає в тому, що воно змушує програміста думати, що насправді потрібно змінити у програмі. Наприклад, введення даних від користувача або читання файлу є операціями, які потрібно обробляти змінно. Також ви можете залишити багато інших операцій незмінними без втрати функціональності.
Завдяки розширеним структурам даних Rust, таким як об’єкти Cell із підрахунком посилань, можна використовувати незмінність у своїх інтересах. Наприклад, цей незмінний однозв’язаний список не має змінних компонентів. Кожна зміна списку по суті передбачає створення нового списку, хоч він і не витрачає пам’ять на копіювання, оскільки наявні дані списку можна використовувати повторно.
Rust має суворі правила власності
Модель керування пам’яттю Rust спирається на сувору концепцію власності. Будь-яке значення в Rust може бути змінено за раз лише однією людиною — його власником. Цей власник може змінюватися протягом життя програми. Але Rust не дозволяє мати більше ніж одного власника одночасно.
Може бути кілька спільних посилань або «запозичень» на щось одночасно заради доступу лише для читання, але може бути лише одне ексклюзивне (читання-редагування) посилання в один момент часу.
Програмісти Rust мають приділяти увагу тому, які дані можна змінювати у який час. Важливо розуміти, що програма, яка порушує ці правила, не призведе до збою, у Rust він навіть не компілюється. Це тому, що правила власності не є обов’язковими.
Інший ефект полягає у тому, що керування пам’яттю Rust під час виконання не обробляється спеціальною системою збирання сміття, як у C# або Go. Такі речі, як динамічний підрахунок посилань, можливі, але вони більше схожі на інтелектуальні покажчики C++, хоча й із кращими засобами керування часом виконання для безпеки потоків.
Право власності не унеможливлює існування умов змагання даних у програмах Rust. Наприклад, доступ до даних між потоками все ще потрібно серіалізувати за допомогою примітивів, таких як блокування або черги, щоб уникнути непередбачуваної поведінки у програмі. Водночас Rust унеможливлює наявність у коді багатьох поширених помилок доступу до пам’яті, які турбують інші мови.
Перевірка запозичень забезпечує дотримання правил власності Rust
Велика частина привабливості Rust полягає в тому, що його правила власності забороняють представляти стани, які можуть спричинити типові помилки пам’яті. Ці проблеми виявляються не під час виконання, а під час компіляції, тому вони ніколи не потрапляють у виробництво.
Недоліком є те, що програмісти Rust витрачають чимало часу, принаймні, коли тільки вивчають мову, з’ясовуючи, як заспокоїти компілятор. Поведінка програми, яка могла залишитися непоміченою в інших мовах (і, можливо, спричинила помилки пам’яті під час виконання), призводить до зупинки компілятора Rust.
Іншим недоліком є те, що ви не можете відмовитися від такої поведінки. Не можна вимкнути перевірку запозичень Rust так, як ви могли б, скажімо, вимкнути лінтер коду для іншої мови. Це покращує програмне забезпечення у довгостроковій перспективі.
Rust дійсно дає змогу відгородити частини вашого коду ключовим словом unsafe
і зняти деякі обмеження, як-от можливість розіменування необробленого покажчика. Але unsafe
не вимикає перевірку запозичень повністю чи чогось подібного. Це для того, щоб взяти код із певною поведінкою, яку ви не бажаєте випадково використовувати, і «шлюзувати» його для безпеки.
Версії Rust підтримують зворотну сумісність
Із моменту виходу Rust 1.0 минуло майже 10 років, і відтоді мова активно розвивалася. З’явилося багато нових функцій, не всі з яких були зворотно сумісні зі старими версіями мови. Щоб ці зміни не ускладнювали підтримку коду у довгостроковій перспективі, Rust представив концепцію мовних версій.
Версії, які виходять приблизно кожні 3 роки, забезпечують гарантію сумісності. Загалом функції, додані до Rust, підтримуватимуться й надалі. Критичні зміни (ті, які є зворотно несумісними) додаються до наступного видання, а не до поточного.
Вибираючи певну версію, ви не прив’язані до неї назавжди. Можна перенести свій код до майбутніх версій за допомогою автоматизованих інструментів і ручної перевірки. Однак є ймовірність того, що зміни, які вас цікавлять, будуть зворотно сумісними із вашим старим кодом, а отже доступними без перенесення версій.
Читайте також на нашому сайті про офіційний реліз Linux Kernel 6.12.
Підписуйтеся на ProIT у Telegram, щоб не пропустити жодної публікації!