Rust (мова програмування)

Матеріал з Вікіпедії — вільної енциклопедії.
Перейти до навігації Перейти до пошуку
Rust
Rust programming language black logo.svg
Парадигма компільована, паралельна, функційна, імперативне, об'єктно-орієнтована, структурна
Дата появи 2010
Творці Graydon Hoare
Розробник Rust Project Developers
Останній реліз 1.65.0 (3 листопада 2022; 26 днів тому (2022-11-03))
Система типізації статична, сильна, вивід типів, структурна
Під впливом від Alef, C#, C++, Camlp4, Common Lisp, Cyclone, Erlang, Haskell, Hermes, Limbo, Napier, Napier88, Newsqueak, NIL, Ocaml, Ruby, Sather, Standard ML
Операційна система Linux, Mac OS X, Windows, FreeBSD
Ліцензія Apache License 2.0 або MIT License[1]
Звичайні розширення файлів .rs
Репозиторій вихідного коду github.com/rust-lang/rust
Вебсайт www.rust-lang.org
CMNS: Rust у Вікісховищі

Rust — мова програмування, яку розробляє Mozilla Research[2]. Мова має сувору типізацію і сфокусована на безпечній роботі з пам'яттю і забезпеченні високого паралелізму виконання завдань (можливість породжувати тисячі і навіть мільйони підпроцесів).

Початковий код проекту поширюються під ліцензією MIT.

Історія[ред. | ред. код]

Робота над мовою була розпочата Грейдоном Гоаром в 2006 році, в 2009[3] до розробки підключилася Mozilla, і в 2010 році мова була офіційно представлена на Mozilla Summit 2010[4]. Мову названо за назвою родини грибів Іржа.[5] У 2010 році розробка мови була переведена з попередньої версії компілятора, яка була написана мовою OCaml, на компілятор, який написаний безпосередньо на Rust, з використанням LLVM як бекенду[6]. У 2011 році новий компілятор успішно скомпілював сам себе[7].

Перший стабільний випуск мови Rust 1.0 відбувся 15 травня 2015[8] після п'яти років розробки, він ознаменував повну стабілізацію програмних інтерфейсів усіх бібліотек і мовних конструкцій. В процесі підготовки гілки Rust 1.0 програмних інтерфейсів і можливості мови піддалися значній ревізії, після якої за умовчанням залишені тільки повністю готові до застосування можливості, реалізація яких не змінюватиметься надалі. Усі інші функції переведені в розряд експериментальних і винесені з постачання за умовчанням.

Паралельно Mozilla Research розвиває експериментальний браузерний рушій Servo, написаний мовою Rust з підтримкою багатониткового рендерингу вебсторінок і розпаралелюванням операцій з DOM (Document Object Model), а компанія Samsung займається його портуванням на Android та ARM процесори[9].

Огляд[ред. | ред. код]

За структурою мова Rust нагадує C++, але істотно відрізняється в деяких деталях реалізації синтаксису і семантики, а також орієнтацією на блокову організацію структури коду, яка дозволяє реалізувати завдання у вигляді легковагих співпрограм. Автоматичне керування пам'яттю позбавляє розробника необхідності маніпулювання вказівниками і захищає від проблем, що виникають через низькорівневу роботу з пам'яттю, таких як звернення до області пам'яті після її звільнення, розіменовування нульових вказівників, вихід за межі буфера тощо. Rust підтримує суміш імперативних процедурних і об'єктно-орієнтованих методів з такими парадигмами, як функційне програмування і модель акторів, а також узагальнене програмування і метапрограмування, в статичних і динамічних стилях.

Особливості[ред. | ред. код]

Базові можливості мови:

Орієнтація на безпеку
  • Акуратна робота з пам'яттю — ніяких нульових і втрачених вказівників. Автоматичне керування пам'яттю без збирача сміття, самими гарантіями компілятора («контролер позичань»);
  • Контроль мінливості. Об'єкти незмінні (Immutable) за умовчанням;
  • Безпека динамічного виконання: обробка збоїв, винятки, ведення логу, RAII/dtors;
  • Typestate: можливість визначення складних інваріантів, що контролюють структури даних.
Орієнтація на паралельність і ефективність коду
  • Явний контроль пам'яті, контролювання схеми розподілу пам'яті;
  • Вкрай легкі завдання, що формуються у вигляді співпрограми. Легкість в породження тисяч і мільйонів підпроцесів;
  • Ітератори стека (фактично лямбда-блоки без розподілу купи);
  • Статична, нативна компіляція із створенням виконуваних файлів ELF, Portable Executable[en], Mach-O;
  • Прямий і простий інтерфейс для коду на мові Сі;
Орієнтація на практичне застосування
  • Мультипарадигмальний, функціональний, імперативно-процедурний, об'єктно-орієнтована, підтримка паралельної actor-моделі;
  • Функції вищого порядку із зв'язуванням (біндінгами);
  • Немає номінальних типів або ієрархії типів;
  • Мульти-платформовий, підтримується Windows, Linux, Mac OS X, *BSD;
  • Зберігання рядків у UTF8, різноманітність низькорівневих типів;
  • Працює з існуючими нативними наборами інструментів: GDB, Valgrind, Shark тощо;
  • Практична можливість порушення правил: можливість ігнорування правил безпеки, якщо чітко вказано, коли і як їх порушувати.

Володіння і контролер позичань[ред. | ред. код]

Виразною особливістю Rust є система володіння даними, забезпечена частиною компілятора, що зветься контролером позичань (borrow checker). Позичанням зветься створення посилання. Правила володіння і позичання:

  • дані завжди ініціалізовані;
  • у даних у кожен момент може бути лише один володілець (змінна);
  • при знищенні володільця (наприклад, при виході з області видимості) дані звільняються;
  • на дані може бути кілька іммутабельних посилань;
  • на дані може бути лише одне мутабельне посилання, якщо немає іммутабельних.

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

Типи даних[ред. | ред. код]

Прості типи
  • цілі - знакові (i8, i16, i32, i64, i128) та беззнакові (u8, u16, u32, u64, u128). Число в назві типу позначає розмір у бітах. Також є архітектурно залежні usize та isize, що представляють розмір у пам'яті;
  • числа з рухомою комою - f32, f64, f128;
  • символ char (4 байти);
  • булевий bool (1 байт).
Складні типи
  • Кортежі (тип, тип, тип, ...) можуть складатися зі значень кількох різних типів. Доступ до елементів через оператор точка з номером, наприклад tuple.0 - нульовий елемент кортежу.
  • Масиви [тип; кількість], наприклад [i64; 10] - масив з 10 чисел i64. Звичайна індексація array[0].
Посилання
  • Посилання &;
  • Мутабельні посилання &mut;
  • Вказівники * - розіменування вказівника є небезпечною (unsafe) операцією;
  • Мутабельні вказівники *mut;
  • Слайси &[] - посилання на масиви, містять посилання на початок і розмір.
Рядки
  • &str - рядковий слайс. Рядки в Rust мають кодування UTF-8 зі змінним розміром символу, тому до них неможливо звертатися за індексом;
  • String - рядок, що зберігає дані в купі.
Користувацькі типи
  • Структура struct
  • Об'єднання union - аналог об'єднання C, де всі елементи розташовані в одному місці в пам'яті. Оскільки компілятор не може гарантувати поточний стан об'єднання, звертатися до його полів - небезпечна (unsafe) операція.
  • Перелічений тип зі станами (тип-сума) enum. Схожий на union, але компілятор гарантує контроль за станом. Украй виразні завдяки мовній конструкції зіставлення з шаблоном.
Складні типи зі стандартної бібліотеки
  • Послідовності: вектор Vec, дек на основі циклічного буфера VecDeque, зв'язний список LinkedList
  • Відображення: на основі геш-таблиці HashMap та на основі дерева BTreeMap
  • Множини: на основі геш-таблиці HashSet та на основі дерева BTreeSet
  • Різне: двійкова купа BinaryHeap
Особливі
  • Одиничний тип - порожній кортеж (), що має одне значення, яке позначається так само (), виконує роль void в C/C++ (позначає функції, що не повертають значення)
  • Тип "ніколи" - позначається !, означає, що функція ніколи не поверне значення; наприклад, функція exit, що завершує роботу програми, має цей тип. Тип "ніколи" є підтипом усіх інших типів Rust, таким чином, будь-яка функція може його "повертати" замість свого основного типу;
  • Option<T> - перелічений тип, що містить два варіанти: Some<T> з типом і None без нього, що позначає відсутність значення; порівняйте з NULL-типами в SQL.
  • Result<T, E> - перелічений тип, що позначає можливість помилки. Містить два варіанти: Ok<T> з типом даних і Err<E> з описом помилки. Використовується замість виключних ситуацій.

Трейти[ред. | ред. код]

Трейти (trait) є однією з виразних особливостей Rust. Трейти в цілому нагадують інтерфейси в мовах програмування, що підтримують ООП, і позначають спільну поведінку різних типів.

Приклади[ред. | ред. код]

Наведені нижче приклади є робочими при збірці за допомогою стабільної версії компілятора Rust 1.63.0, редакції 2021.

Hello world:

fn main() {
    println!("hello, world");
}

Три версії реалізації функції пошуку факторіала, в рекурсивному та ітеративному стилях:

// Умовний оператор, що показує можливість неявного повернення значення (implicit return).
// На відміну від C++ і схожих мов, у Rust оператор «if» насправді є виразом, і може повертати значення.
// Якщо у функції не вказано явного return, повертається останнє значення в тілі функції.
fn recursive_factorial(n: u32) -> u32 {
    if n <= 1 {
        1
    } else {
        n * recursive_factorial(n - 1)
    }
}

fn iterative_factorial(n: u32) -> u32 {
    // Змінні проголошуються ключовим словом `let`.
    // Ключове слово `mut` робить змінні мутабельними (дозволяє змінюватися)
    let mut i = 1u32;
    let mut result = 1u32;
    while i <= n {
        result *= i;
        i += 1;
    }
    return result; // Явне повернення значення, на відміну від попередньої функції
}

fn iterator_factorial(n: u32) -> u32 {
    // Ітератори мають багато методів для трасформації
    // |accum, x| визначає анонімну функцію.
    // Оптимізації на кшталт вбудування тіла функції дозволяють інтервалу
    // і fold досягати продуктивності, подібної до iterative_factorial.
    (1..=n).fold(1, |accum, x| accum * x)
}

fn main() {
    println!("Recursive result: {}", recursive_factorial(10));
    println!("Iterative result: {}", iterative_factorial(10));
    println!("Iterator result: {}", iterator_factorial(10));
}

Демонстрація вбудованих в Rust унікальних розумних вказівників, разом з тип-сумами[en] та методами:

use IntList::{Node, Empty};

// This program defines a recursive datastructure and implements methods upon it.
// Recursive data structures require a layer of indirection, which is provided here
// by a unique pointer, constructed via the `Box::new` constructor. These are
// analogous to the C++ library type `std::unique_ptr`, though with more static
// safety guarantees.
fn main() {
    let list = IntList::new().prepend(3).prepend(2).prepend(1);
    println!("Sum of all values in the list: {}.", list.sum());
    println!("Sum of all doubled values in the list: {}.", list.multiply_by(2).sum());
}

// `enum` defines a tagged union that may be one of several different kinds of values at runtime.
// The type here will either contain no value, or a value and a pointer to another `IntList`.
enum IntList {
    Node(i32, Box<IntList>),
    Empty
}

// An `impl` block allows methods to be defined on a type.
impl IntList {
    fn new() -> Box<IntList> {
        Box::new(Empty)
    }

    fn prepend(self, value: i32) -> Box<IntList> {
        Box::new(Node(value, Box::new(self)))
    }

    fn sum(&self) -> i32 {
        // `match` expressions are the typical way of employing pattern-matching,
        // and are somewhat analogous to the `switch` statement from C and C++.
        match *self {
            Node(value, ref next) => value + next.sum(),
            Empty => 0
        }
    }

    fn multiply_by(&self, n: i32) -> Box<IntList> {
        match *self {
            Node(value, ref next) => Box::new(Node(value * n, next.multiply_by(n))),
            Empty => Box::new(Empty)
        }
    }
}

Проста демонстрація легковагих можливостей паралелізму Rust:

// Ця функція створює десять конкурентно виконуваних потоків.
// Для перевірки можете запустити програму кілька разів і побачити 
// зміну порядку, в якому виводяться повідомлення різних потоків.
fn main() {
    // Ця стрічка іммутабельна і тому може різні потоки можуть отримувати доступ до неї
    let greeting = "Hello";

    // Функція scope створює потоки, що не будуть знищені до кінця своєї роботи
    //аргумент анонімної функції - об'єкт типу Scope, який і триматиме потоки
    std::thread::scope(|s| { 
        for num in 0..10 {
            //move визначає захоплення за значенням
            s.spawn(move || { 
                // `println!` - це макрос, що формує виведення за стрічкою форматування під час компіляції
                // Макроси в Rust структурні (як у Scheme), а не текстові (як у C).
                println!("{greeting} from thread number {num}");
            });
        }
    });
}

Українська спільнота Rust[ред. | ред. код]

Існує й активно розвивається українська гілка Rust-спільноти. Метою спільноти є популяризація Rust в Україні та розвиток спільноти навколо нього, запуск або сприяння розвитку всеукраїнських менторських та освітніх програм, залучення проєктів на Rust та пов’язаних з ними інвестицій в Україну[10].

Створено підбірку навчальних матеріалів, завершується робота над перекладом підручника.[коли?]

Примітки[ред. | ред. код]

  1. COPYRIGHT. Rust compiler source repository. Процитовано 17 грудня 2012. 
  2. The Rust Language. Lambda the Ultimate. 8 липня 2010. Архів оригіналу за 23 листопада 2012. Процитовано 30 жовтня 2010. 
  3. Project FAQ. 14 вересня 2010. Архів оригіналу за 20 липня 2020. Процитовано 17 квітня 2012. 
  4. Future Tense. 29 квітня 2011. Архів оригіналу за 18 вересня 2012. Процитовано 17 квітня 2012. «At Mozilla Summit 2010, we launched Rust, a new programming language motivated by safety and concurrency for parallel hardware, the “manycore” future which is upon us.» 
  5. Hoare, Graydon (7 червня 2014). Internet archaeology: the definitive, end-all source for why Rust is named "Rust". Reddit.com. Архів оригіналу за 14 липня 2016. Процитовано 4 лютого 2018. 
  6. Hoare, Graydon (2 жовтня 2010). Rust Progress. Архів оригіналу за 18 вересня 2012. Процитовано 17 квітня 2012. 
  7. Hoare, Graydon (20 квітня 2011). [rust-dev] stage1/rustc builds. Архів оригіналу за 20 липня 2011. Процитовано 17 квітня 2012. «After that last change fixing the logging scope context bug, looks like stage1/rustc builds. Just shy of midnight :)» 
  8. Announcing Rust 1.0. Архів оригіналу за 15 травня 2015. Процитовано 16 травня 2015. 
  9. Samsung teams up with Mozilla to build browser engine for multicore machines. Ars Technica. Архів оригіналу за 16 грудня 2016. Процитовано 24 жовтня 2015. 
  10. Rust Ukraine. Розповідаємо про наше ком’юніті. dou.ua. Архів оригіналу за 12 червня 2022. Процитовано 13 вересня 2022. 

Посилання[ред. | ред. код]