Перейти до вмісту

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

Матеріал з Вікіпедії — вільної енциклопедії.
Rust
Парадигмаімперативна, рівночасна, структурна, узагальнена, функційна
Дата появи2010
ТворціҐрейдон Гоар
РозробникRust Project Developers
Останній реліз1.85.0[1] Редагувати інформацію у Вікіданих (20 лютого 2025)
Система типізаціїафінна[en], вивідна, іменна[en], статична, строга
Під впливом відAlef, C#, C++, Cyclone, Elm, Erlang, Haskell, Limbo, Newsqueak, OCaml, Ruby, Scheme, Standard ML, Swift
Мова реалізаціїRust
ПлатформаWindows[2], Linux[2], macOS[2], FreeBSD[2], iOS[2], Android[2], кросплатформова програма[2] і WebAssembly[2] Редагувати інформацію у Вікіданих
Операційна системаLinux, macOS, Windows, FreeBSD
ЛіцензіяApache License 2.0 або ліцензія MIT[3]
Звичайні розширення файлів.rs .rlib
Репозиторій вихідного кодуgithub.com/rust-lang/rust Редагувати інформацію у Вікіданих
Вебсайтwww.rust-lang.org

Rust (укр. Раст, дос.«Іржа») — сучасна багатопарадигмова мова програмування загального призначення. Мова має сильну (строгу) типізацію і зосереджена на безпечній роботі з пам'яттю та забезпеченні високої рівночасности виконання задач (можливість породжувати тисячі й навіть мільйони підпроцесів).

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

Історія

[ред. | ред. код]

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

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

Опоруч Mozilla Research розвиває експериментальний браузерний рушій Servo, написаний мовою Rust з підтримкою багатопотокового рендерингу вебсторінок і розрівнобіжненням операцій з DOM, а компанія Samsung займається його портуванням на Android та ARM процесори[11].

Огляд

[ред. | ред. код]

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

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

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

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

Синтаксис та особливості

[ред. | ред. код]

Особливості мови

[ред. | ред. код]

Основні можливості мови:

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

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

[ред. | ред. код]

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

Правила володіння і позичання:

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

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

Ознаки

[ред. | ред. код]

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

Типи даних

[ред. | ред. код]

Прості типи

[ред. | ред. код]
Прості (примітивні) типи мови Rust
Тип Опис Приклад
  • i8
  • i16
  • i32
  • i64
  • i128
  • 7
  • 7i128
  • u8
  • u16
  • u32
  • u64
  • u128
  • 14
  • 14u128
  • isize
  • usize
Цілі числа розміром із вказівник (розмір залежить від платформи)
  • -2isize
  • 14usize
  • f32
  • f64
  • f128
Числа з рухомою комою -3f32
char Символ (займає 4 байти)
  • 'a'
  • '语'
  • '🦀'
  • '\u{200D}'
str
  • Рядковий зріз
  • Рядки мають кодування UTF-8 зі змінним розміром символу, тому до них неможливо звертатися за індексом
  • Часто трапляється у своїй позиченій формі &str
  • "Hello"
  • "3"
  • "🦀🦀🦀"
bool Булевий тип (займає 1 байт)
  • true
  • false
[T] Зріз — динамічно змінюване представлення в суміжній послідовності
  • [1, 2, 3, 4, 5][..i]
  • "Hello, world!".as_bytes()
  • vec![1, 2, 3].as_slice()
! Тип «ніколи»[en] let x = { return 123 };

Складені типи

[ред. | ред. код]
Складені типи мови Rust
Тип Опис Приклад
(T, U, …)
  • Кортеж, або n-ка, — скінченна неоднорідна послідовність
  • Доступ до елементів здійснюється через оператор крапка з числом, наприклад tuple.0 — нульовий елемент кортежу
[T; N]
  • Масив — колекція з N об'єктів одного типу T, що зберігаються в суміжній пам'яті
  • Індексування здійснюється через дужковий запис, наприклад array[0]
[i64; 10] (масив з 10 чисел типу i64)

Типи зі стандартної бібліотеки

[ред. | ред. код]
Типи зі стандартної бібліотеки мови Rust
Тип Опис Приклад
String Динамічний рядок, що зберігає дані в купі
  • String::new()
  • String::from("Hello")
  • "🦀🦀🦀".to_string()
Option<T> Тип-опція[en]
  • None
  • Some(3)
  • Some("hello")
Result<T, E> Обробляння помилок
  • Result::Ok(3)
  • Result::Err("something went wrong")
Vec<T> Вектор (динамічний масив)
  • Vec::new()
  • vec![1, 2, 3, 4, 5]
VecDeque Двобічна черга на основі циклічного буфера
let mut buf = VecDeque::new();
buf.push_back(3);
buf.push_back(4);
buf.push_back(5);
assert_eq!(buf.get(1), Some(&4));
LinkedList<T> Зв'язаний список
let mut my_list = LinkedList::new();
my_list.push_back('b');
my_list.push_back('c');
HashMap<K, V> Геш-таблиця
let mut player_stats = HashMap::new();
player_stats.insert("damage", 1);
player_stats.entry("health").or_insert(100);
BTreeMap<K, V> Б-дерево
let mut solar_distance = BTreeMap::from([
    ("Mercury", 0.4),
    ("Venus", 0.7),
]);
solar_distance.entry("Earth").or_insert(1.0);
HashSet Множина на основі геш-таблиці
let mut books = HashSet::new();

books.insert("A Dance With Dragons".to_string());
books.insert("To Kill a Mockingbird".to_string());
books.insert("The Odyssey".to_string());
BTreeSet Множина на основі Б-дерева
let mut planets = BTreeSet::new();
planets.insert("Mars");
planets.insert("JUpiter");
BinaryHeap Двійкова купа
let mut heap = BinaryHeap::new();

heap.push(1);
heap.push(5);
heap.push(2);

Вказівники

[ред. | ред. код]
Вказівникові та посилальні типи мови Rust
Тип Опис Приклад
  • &T
  • &mut T
Посилання (немінливі та мінливі)
let x_ref = &x;
let x_ref = &mut x;
  • *const T
  • *mut T
  • Вказівники (немінливі та мінливі)
  • Розіменування вказівника є небезпечною (unsafe) операцією
let x_ptr = &x as *const T;
let x_ptr = &mut x as *mut T;

Задані користувачем типи

[ред. | ред. код]
Задані користувачем типи мови Rust
Тип Опис
struct Структура
union
  • Об'єднання — відповідник об'єднання C, де всі елементи розташовані в одному місці в пам'яті
  • Оскільки компілятор не може ручатися за поточний стан об'єднання, звертатися до його полів — небезпечна (unsafe) операція
enum

Приклади

[ред. | ред. код]

Наведені нижче приклади є робочими під час збирання з використанням стабільної версії компілятора 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};

// Ця програма задає рекурсивну структуру даних та реалізує для неї методи.
// Рекурсивні структури даних потребуть шару розіменування, який тут забезпечується
// унікальним вказівником, побудованим за допомогою конструктора `Box::new`. Вони
// аналогічні бібліотечному типу C++ `std::unique_ptr`, хоча й мають більше статичних
// гарантій безпеки.
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` задає тип-суму, що може бути одним з декількох різних видів значень під час виконання.
// Тут тип або не міститиме значення, або міститиме значення і вказівник на інший `IntList`.
enum IntList {
    Node(i32, Box<IntList>),
    Empty
}

// Блок `impl` дає змогу задавати методи для типу.
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` є типовим способом застосування зіставлення із взірцем
        // і дещо схожі на інструкцію `switch` із C та 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}");
            });
        }
    });
}

Примітки

[ред. | ред. код]
  1. Announcing Rust 1.85.0 and Rust 2024 (англійська мова) . 20 лютого 2025. Процитовано 20 лютого 2025.
  2. а б в г д е ж и https://doc.rust-lang.org/rustc/platform-support.html
  3. COPYRIGHT. Rust compiler source repository. Процитовано 17 грудня 2012.
  4. Project FAQ. 14 вересня 2010. Архів оригіналу за 20 липня 2020. Процитовано 17 квітня 2012.
  5. 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.
  6. Hello World!. Rust Foundation (англ.). 8 лютого 2020. Архів оригіналу за 19 квітня 2022. Процитовано 4 червня 2022.
  7. Hoare, Graydon (7 червня 2014). Internet archaeology: the definitive, end-all source for why Rust is named "Rust". Reddit.com. Архів оригіналу за 14 липня 2016. Процитовано 4 лютого 2018.
  8. Hoare, Graydon (2 жовтня 2010). Rust Progress. Архів оригіналу за 18 вересня 2012. Процитовано 17 квітня 2012. [Архівовано 2014-08-15 у Wayback Machine.]
  9. 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 :)
  10. Announcing Rust 1.0. Архів оригіналу за 15 травня 2015. Процитовано 16 травня 2015.
  11. Samsung teams up with Mozilla to build browser engine for multicore machines. Ars Technica. Архів оригіналу за 16 грудня 2016. Процитовано 24 жовтня 2015.

Посилання

[ред. | ред. код]