Rust мова. Мова Rust і чому її треба з'їсти. Дрібні, але цікаві синтаксичні особливості

Головна / Оптимізація роботи

Rust- Нова експериментальна мова програмування, що розробляється Mozilla. Мова компилювана і мультипарадигмальна, позиціонується як альтернатива С/С++, що вже саме по собі цікаво, тому що навіть претендентів на конкуренцію не так уже й багато. Можна згадати D Вальтера Брайта чи Go від Google.
У Rust підтримуються функціональне, паралельне, процедурне та об'єктно-орієнтоване програмування, тобто. майже весь спектр парадигм, що реально використовуються в прикладному програмуванні.

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

Перше враження

Синтаксис мови будується в традиційному сі-подібному стилі (що не може не тішити, оскільки це вже стандарт де-факто). Звісно, ​​всім відомі помилки дизайну С/С++ враховано.
Традиційний Hello World виглядає так:
use std; fn main(args: ) ( std::io::println("hello world from " + args + "!"); )

Приклад трохи складніший – функція розрахунку факторіалу:

Fn fac(n: int) -> int ( let result = 1, i = 1; while i<= n { result *= i; i += 1; } ret result; }

Як очевидно з прикладу, функції оголошуються у «функціональному» стилі (такий стиль має деякі переваги перед традиційним «int fac(int n)»). Бачимо автоматичне виведення типів(ключове слово let), відсутність круглих дужок аргументу while (аналогічно Go). Ще відразу впадає у вічі компактність ключових слів. Творці Rust дійсно цілеспрямовано зробили всі ключові слова якомога коротшими, і, скажу чесно, мені це подобається.

Дрібні, але цікаві синтаксичні особливості

  • У числові константи можна вставляти підкреслення. Зручна штука, зараз цю можливість додають у багато нових мов.
    0xffff_ffff_ffff_ffff_ffff_ffff
  • Двійкові константи. Звичайно, справжній програміст повинен перетворювати bin в hex в розумі, але так зручніше! 0b1111_1111_1001_0000
  • Тіла будь-яких операторів (навіть що складаються з єдиного виразу) мають бути обов'язково поміщені у фігурні дужки. Наприклад, Сі можна було написати if(x>0) foo(); , в Rust потрібно обов'язково поставити фігурні дужки навколо foo()
  • Натомість аргументи операторів if, while та подібних не потрібно укладати у коло дужки.
  • у багатьох випадках блоки коду можуть розглядатися як вирази. Зокрема, можливе наприклад таке:
    let x = if the_stars_align() ( 4 ) else if something_else() ( 3 ) else ( 0 );
  • синтаксис оголошення функцій - спочатку ключове слово fn, потім список аргументів, тип аргументу вказується після імені, потім, якщо функція повертає значення - стрілочка "->" і тип значення, що повертається
  • аналогічно оголошуються змінні: ключове слово let, ім'я змінної, після змінної можна через двокрапку уточнити тип, а потім - присвоїти початкове значення.
    let count: int = 5;
  • за замовчуванням усі змінні незмінні; для оголошення змінних змінних використовується ключове слово mutable.
  • імена базових типів - найкомпактніші з усіх, які мені зустрічалися: i8, i16, i32, i64, u8, u16, u32, u64, f32, f64
  • як було зазначено вище, підтримується автоматичний висновок типів
У мові присутні вбудовані засоби налагодження програм:
Ключове слово failзавершує поточний процес
Ключове слово logвиводить будь-який вираз мови в лог (наприклад, в stderr)
Ключове слово assertперевіряє вираз, і якщо воно хибне, завершує поточний процес
Ключове слово noteдозволяє вивести додаткову інформацію у разі аварійного завершення процесу.

Типи даних

Rust, подібно до Go, підтримує структурну типізацію(хоча, за твердженням авторів, мови розвивалися незалежно, тому цей вплив їх спільних попередників - Alef, Limbo і т.д.). Що таке структурна типізація? Наприклад, у вас у якомусь файлі оголошено структуру (або, у термінології Rust, «запис»)
type point = (x: float, y: float);
Ви можете оголосити купу змінних і функції з типами аргументів «point». Потім, десь в іншому місці, ви можете оголосити якусь іншу структуру, наприклад
MySuperPoint = (x: float, y: float);
та змінні цього типу будуть повністю сумісні зі змінними типу point.

На противагу цьому, номінативна типізація, прийнята С, С++, C# і Java таких конструкцій не допускає. За номінативної типізації кожна структура - це унікальний тип, за умовчанням несумісний з іншими типами.

Структури в Rust називаються записи (record). Також є кортежі - це самі записи, але з безіменними полями. Елементи кортежу, на відміну елементів запису, неможливо змінити.

Є вектори - в чомусь подібні до звичайних масивів, а в чомусь - типу std::vector з stl. При ініціалізації списком використовуються квадратні дужки, а не фігурні як С/С++

Let myvec =;

Вектор проте - динамічна структура даних, зокрема, вектора підтримують конкатенацію.

Let v: mutable =; v + =;

Існують шаблони. Їхній синтаксис цілком логічний, без нагромаджень «template» із С++. Підтримуються шаблони функцій та типів даних.

Fn for_rev (v: [T], act: block(T)) ( let i = std::vec::len(v); while i > 0u ( i -= 1u; act(v[i]); ) ) type circular_buf = (start: uint, end: uint, buf:);

Мова підтримує так звані теги. Це не що інше, як union з Сі, з додатковим полем - кодом варіанта, що використовується (тобто щось спільне між об'єднанням і перерахуванням). Або, з погляду теорії – алгебраїчний тип даних.

Tag shape ( circle(point, float); rectangle(point, point); )

У найпростішому випадку тег ідентичний перерахунку:

Tag animal ( dog; cat; ) let a: animal = dog; a = cat;
У складніших випадках кожен елемент «перерахування» - самостійна структура, має свій «конструктор».
Ще цікавий приклад – рекурсивна структура, за допомогою якої задається об'єкт типу «список»:
tag list ( nil; cons (T, @ list ); ) let a: list = cons(10, @cons(12, @nil));
Теги можуть брати участь у виразах зіставлення зі зразком, які можуть бути складними.
alt x ( cons(a, @cons(b, _)) ( process_pair(a,b); ) cons(10, _) ( process_ten(); ) _ ( fail; ) )

Зіставлення із зразком (pattern matching)

Для початку можна розглядати патерн матчингу як покращений switch. Використовується ключове слово alt, після якого слідує аналізований вираз, а потім у тілі оператора - патерни та дії у разі збігу з патернами.
alt my_number ( 0 ( std::io::println("zero"); ) 1 | 2 ( std::io::println("one or two"); ) 3 to 10 ( std::io::println ("three to ten"); ) _ ( std::io::println("something else"); ) )
Як «патерони» можна використовувати не тільки константи (як у Сі), але й складніші вирази – змінні, кортежі, діапазони, типи, символи-заповнювачі (placeholders, "_"). Можна прописувати додаткові умови за допомогою оператора when, наступного відразу за патерном. Існує спеціальний варіант оператора для матчингу типів. Таке можливо, оскільки в мові є універсальний варіантний тип anyоб'єкти якого можуть містити значення будь-якого типу.

Покажчики.Крім звичайних "сишних" покажчиків, в Rust підтримуються спеціальні "розумні" покажчики з вбудованим підрахунком посилань - розділяються (Shared boxes) та унікальні (Unique boxes). Вони в чомусь подібні до shared_ptr і unique_ptr із С++. Вони мають свій синтаксис: @ для поділяються і ~ для унікальних. Для унікальних покажчиків замість копіювання існує спеціальна операція – переміщення:
let x = ~10; let y<- x;
після такого переміщення покажчик x деініціалізується.

Замикання, часткове застосування, ітератори

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

1. Ключове слово lambdaвикористовується для оголошення вкладеної функції чи функціонального типу даних.

Fn make_plus_function(x: int) -> lambda(int) -> int ( lambda(y: int) -> int ( x + y ) ) let plus_two = make_plus_function(2); assert plus_two(3) == 5;

У цьому прикладі ми маємо функцію make_plus_function, яка приймає один аргумент "x" типу int і повертає функцію типу "int->int" (тут lambda - ключове слово). У тілі функції описується ця функція. Трохи спантеличує відсутність оператора «return», втім, для ФП це звичайна справа.

2. Ключове слово blockвикористовується для оголошення функціонального типу - аргументу функції, якою можна підставити щось, схоже на блок звичайного коду.
fn map_int(f: block(int) -> int, vec: ) -> ( let result = ;

Тут ми маємо функцію, на вхід якої подається блок - по суті лямбда-функція типу «int->int», та вектор типу int (про синтаксис векторів далі). Сам «блок» в коді, що викликає, записується за допомогою кілька незвичайного синтаксису (| x | x + 1). Особисто мені більше подобаються лямбди C#, символ | вперто сприймається як бітове АБО (яке, до речі, в Rust також є, як і всі старі добні сишні операції).

3. Часткове застосування - створення функції на основі іншої функції з великою кількістю аргументів шляхом вказівки значень деяких аргументів цієї іншої функції. Для цього використовується ключове слово bindта символ-заповнювач "_":

Let daynum = bind std::vec::position(_, ["mo", "tu", "we", "do", "fr", "sa", "su"])

Щоб було зрозуміліше, скажу відразу, що таке можна зробити на звичайному Сі шляхом створення найпростішої обгортки, якось так:
const char * daynum (int i) (const char * s = ("mo", "tu", "we", "do", "fr", "sa", "su"); return s [i]; )

Але часткове застосування - це функціональний стиль, а не процедурний (до речі, з наведеного прикладу неясно, як зробити часткове застосування, щоб отримати функцію без аргументів)

Ще приклад: оголошується функція add з двома аргументами int, що повертає int. Далі оголошується функціональний тип single_param_fn, що має один аргумент int і повертає int. За допомогою bind оголошуються два функціональні об'єкти add4 та add5, побудовані на основі функції add, у якої частково задані аргументи.

Fn add(x: int, y: int) -> int ( ret x + y; ) type single_param_fn = fn(int) -> int; let add4: single_param_fn = bind add(4, _); let add5: single_param_fn = bind add(_, 5);

Функціональні об'єкти можна викликати так само, як і звичайні функції.
assert (add(4,5) == add4(5)); assert (add(4,5) == add5(4));

4. Чисті функції та предикати
Чисті (pure) функції - це функції, які мають побічних ефектів (зокрема які викликають жодних інших функцій, крім чистих). Такі функції виділяються ключовим словом pure.
pure fn lt_42(x: int) -> bool ( ret (x< 42); }
Предикати – це чисті (pure) функції, що повертають тип bool. Такі функції можуть використовуватися в системі типудержави (див. далі), тобто викликатися на етапі компіляції для різних статичних перевірок.

Синтаксичні макроси
Запланована фіча, але дуже корисна. У Rust вона поки що на стадії початкової розробки.
std::io::println(#fmt("%s is %d", "the answer", 42));
Вираз, аналогічний сишному printf, але виконується під час компіляції (відповідно, всі помилки аргументів виявляються стадії компіляції). На жаль, матеріалів по синтаксичним макросам вкрай мало, та й самі вони перебувають у стадії розробки, але є надія, що вийде щось типу макросів Nemerle.
До речі, на відміну від того ж Nemerle, рішення виділити макроси синтаксично за допомогою символу # вважаю дуже грамотним: макрос - це сутність, дуже відрізняється від функції, і я вважаю важливим з першого погляду бачити, де в коді викликаються функції, а де - макроси.

Атрибути

Концепція, схожа на атрибути C# (і навіть із схожим синтаксисом). За це розробникам окреме спасибі. Як і слід очікувати, атрибути додають метаінформацію до тієї сутності, яку вони анотують,
# fn register_win_service() ( /* ... */ )
Придуманий ще один варіант синтаксису атрибутів - той самий рядок, але з точкою з комою в кінці, анотує поточний контекст. Тобто те, що відповідає найближчим фігурним дужкам, які охоплюють такий атрибут.
fn register_win_service() ( #; /* ... */ )

Паралельні обчислення

Мабуть, одна з найбільш цікавих елементів мови. При цьому в tutorial на даний момент не описано взагалі:)
Програма на Rust складається з «дерева завдань». Кожне завдання має функцію входу, власний стек, засоби взаємодії з іншими завданнями - канали для вихідної інформації та порти для вхідної, і володіє деякою частиною об'єктів у динамічній купі.
Багато завдань Rust можуть існувати в рамках одного процесу операційної системи. Завдання Rust «легковажні»: кожне завдання споживає менше пам'яті ніж процес ОС, і перемикання між ними здійснюється швидше ніж перемикання між процесами ОС (тут, мабуть, маються на увазі все-таки «потоки»).

Завдання складається щонайменше з однієї функції без аргументів. Запуск завдання здійснюється за допомогою функції spawn. Кожне завдання може мати канали, за допомогою яких вона передає інформацію іншим завданням. Канал – це спеціальний шаблонний тип chan, що параметризується типом даних каналу. Наприклад, chan - канал передачі беззнакових байтів.
Для передачі канал використовується функція send, першим аргументом якої є канал, а другим - значення передачі. Фактично ця функція містить значення у внутрішній буфер каналу.
Для отримання даних використовуються порти. Порт - це шаблонний тип port, що параметризується типом даних порту: port - порт прийому беззнакових байтів.
Для читання з портів використовується функція recv, аргументом якої є порт, а значенням, що повертається - дані з порту. Читання блокує завдання, тобто. якщо порт порожній, завдання переходить у стан очікування до того часу, поки інше завдання відправить на пов'язаний з портом канал дані.
Зв'язування каналів із портами відбувається дуже просто - шляхом ініціалізації каналу портом за допомогою ключового слова chan:
let reqport = port();
let reqchan = chan (reqport);
Декілька каналів можуть бути підключені до одного порту, але не навпаки - один канал не може бути підключений одночасно до кількох портів.

Typestate

Загальноприйнятого перекладу на російську поняття «typestate» я так і не знайшов, тому називатиму це «стану типів». Суть цієї фічі в тому, що, крім звичайного контролю типів, прийнятого в статичній типізації, можливі додаткові контекстні перевірки на етапі компіляції.
У тому чи іншому вигляді стану типів знайомі всім програмістам - за повідомленнями компілятора "змінна використовується без ініціалізації". Компілятор визначає місця, де змінна, в яку жодного разу не було запису, використовується для читання та видає попередження. У більш загальному вигляді ця ідея виглядає так: кожен об'єкт має набір станів, які він може приймати. У кожному стані для цього об'єкта визначено допустимі та неприпустимі операції. І компілятор може виконувати перевірки - чи допустима конкретна операція над об'єктом у тому чи іншому місці програми. Важливо, що це перевірки виконуються на етапі компіляції.

Наприклад, якщо у нас є об'єкт типу "файл", то у нього може бути стан "закритий" та "відкритий". І операція читання із файлу неприпустима, якщо файл закрито. У сучасних мовах зазвичай функція читання або кидає виняток або повертає код помилки. Система станів типів могла б виявити таку помилку на етапі компіляції - подібно до того, як компілятор визначає, що операція читання змінної відбувається до будь-якої можливої ​​операції запису, він міг би визначити, що метод «Read», допустимий у стані «файл відкритий», викликається до методу "Open", що переводить об'єкт у цей стан.

У Rust існує поняття "предикати" - спеціальні функції, що не мають побічних ефектів і повертають тип bool. Такі функції можуть бути використані компілятором для виклику на етапі компіляції з метою статичних перевірок тих чи інших умов.

Обмеження (constraints) – це спеціальні перевірки, які можуть виконуватися на етапі компіляції. Для цього використовується ключове слово Check.
pure fn is_less_than(int a, int b) -< bool { ret a < b; } fn test() { let x: int = 10; let y: int = 20; check is_less_than(x,y); }
Предикати можуть «навішуватися» на вхідні параметри функцій таким способом:
fn test(int x, int y) : is_less_than(x,y) ( ... )

Інформації щодо типудержави вкрай мало, тому багато моментів поки що незрозумілі, але концепція в будь-якому випадку цікава.

На цьому все. Цілком можливо, що я все-таки пропустив якісь цікаві моменти, але стаття і так роздулася. За бажання можна вже зараз зібрати компілятор Rust та спробувати погратися з різними прикладами. Інформація зі збирання наведена на

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

Спільнота Rust останнім часом сконцентрувала багато своїх зусиль на асинхронному вводі/виводі, реалізованому у вигляді бібліотеки Tokio. І це чудово.

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

  • Що це таке - Async I/O?
  • Що таке корутини ( coroutines )?
  • Що таке легковагі потоки ( lightweight threads )?
  • Що таке футури? futures )?

  • Як вони поєднуються між собою?

Я покажу вам, як написати невелику програму, яка завантажує стрічку ( feed) у форматі JSON, парсит та виводить список нотаток на консоль у форматованому вигляді.

У нас все вилилося в дуже короткий код. Як? Дивіться під катом.

Ключове слово unsafe є невід'ємною частиною дизайну Rust. Для тих хто не знайомий з ним: unsafe - це ключове слово, яке, говорячи простою мовою, є способом обійти перевірку типів( type checking) Rust'а.

Існування ключового слова unsafe для багатьох спочатку є несподіванкою. Справді, хіба те, що програми не падають від помилок при роботі з пам'яттю, не є особливістю Rust? Якщо так, то чому є легкий спосіб обійти систему типів? Це може бути дефектом дизайну мови.

Все ж таки, на мою думку, unsafe не є недоліком. Насправді вона є важливою частиною мови. Unsafe виконує роль деякого вихідного клапана - це означає те, що ми можемо використовувати систему типів у простих випадках, проте дозволяючи використовувати всілякі хитрі прийоми, які ви хочете використовувати у вашому коді. Ми тільки вимагаємо, щоб ви приховували ці ваші прийоми (unsafe код) за безпечними зовнішніми абстракціями.

Ця нотатка представляє ключове слово unsafe та ідею обмеженої «небезпеки». Фактично це провісник нотатки, яку я сподіваюся написати трохи згодом. Вона обговорює модель пам'яті Rust, яка вказує, що можна, а що не можна робити в коді unsafe.

Будучи новачком у Rust, я заплутувався у різних способах уявлення рядків. У книзі про мову Rust є розділ "References and Borrowing", в якій використовується три різних типи рядкових змінних у прикладах: String, & String і & str.

Почнемо з різниці між str і String: String - це структура даних, що розширюється, що виділяється на купі, тоді як str - це незмінний рядок фіксованої довжини, десьв пам'яті.

Багато програмістів вже вміють програмувати об'єктно-орієнтованими мовами. Rust не є класичною об'єктно-орієнтованою мовою, але основні інструменти ООП можна застосовувати і в ньому.

У цій статті ми розглянемо, як програмувати на Rust в стилі ООП. Ми робитимемо це на прикладі: побудуємо ієрархію класів у навчальному завданні.

Наше завдання – це робота з геометричними фігурами. Ми будемо виводити їх на екран у текстовому вигляді та обчислювати їхню площу. Наш набір фігур – прямокутник, квадрат, еліпс, коло.

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

У цій статті я хочу обговорити шаблон проектування новий тип(newtype), а також типажі From та Into, які допомагають у перетворенні типів.

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

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

Один цікавий шаблон, що часто застосовується до таких проблем - «Кінцевий автомат». Пропоную витратити трохи часу, щоб зрозуміти, що саме мається на увазі під цим словосполученням, і чому це так цікаво.

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

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

Реалізація арифметики натуральних чисел за допомогою чисел Пеано – популярне завдання навчання програмування. Мені було цікаво, чи можна їх реалізувати на Rust.

Таким чином, моє завдання: записати і скласти натуральні числа з перевіркою на рівні типів.

Якщо вірити вікіпедії «Аксіоми Пеано - одна із систем аксіом для натуральних чисел, введена в XIX столітті італійським математиком Джузеппе Пеано.»

Нас цікавлять дві з них - за допомогою яких можна ввести та використовувати натуральні числа:

  • 1 є натуральним числом
  • Число, що йде за натуральним, теж є натуральним.

Дослівно запишемо на rust за допомогою:

1 2 3 4 enum Nat (Zero, Succ (Nat))

Nat - це або нуль, або таке натуральне число.

Зауваження: проект futures-rs був реорганізований і багато речей було перейменовано. Де можливо, посилання було оновлено.

Починаємо роботу з futures

Цей документ допоможе вам вивчити контейнер для мови програмування Rust - futures, яка забезпечує реалізацію futures та потоків з нульовою вартістю. Futures доступні в багатьох інших мовах програмування, таких як C++, Java, і Scala, і futures контейнер черпає натхнення з бібліотек цих мов. Однак він відрізняється ергономічністю, а також дотримується філософії абстракцій з нульовою вартістю, властивою Rust, а саме: для створення та композиції futures не потрібно виділення пам'яті, а для Task, що керує ними, потрібна лише одна аллокація. Futures повинні стати основою асинхронного компонованого високопродуктивного введення/виводу в Rust, і ранні виміри продуктивності показують, що простий сервер HTTP, побудований на futures, дійсно швидкий.

Ця документація поділена на кілька розділів:

  • «Привіт, світ!»;
  • типу future;
  • типаж Stream;
  • конкретні futures і потік (Stream);
  • повернення майбутніх;
  • Task та future;
  • локальні дані завдання.

Зауваження: проект futures-rs був реорганізований і багато речей було перейменовано. Де можливо, посилання було оновлено.

Одним з основних прогалин в екосистемі Rust був швидкий та ефективний асинхронне введення/виведення. У нас є міцний фундамент із бібліотеки mio, але вона дуже низькорівнева: доводиться вручну створювати кінцеві автомати та жонглювати зворотними викликами.

Нам би хотілося чогось більш високорівневого, з кращою ергономікою, але щоб воно мало хорошу компонованістюпідтримуючи екосистему асинхронних абстракцій, що працюють разом. Звучить дуже знайомо: ту ж мету переслідувало впровадження futures(або promises) у багато мов , що підтримують синтаксичний цукор у вигляді async/awaitна вершині.

Примітивні цілі численні типи, підтримувані процесорами, є обмеженим наближенням до нескінченного набору цілих чисел, якими ми звикли оперувати в реальному житті. Це обмежене уявлення не завжди збігається з реальними числами, наприклад 255_u8 + 1 == 0 . Найчастіше програміст забуває про цю різницю, що може призводити до багам.

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

Трохи про Iron

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

Філософія

Iron побудований на принципі розширюваності настільки, наскільки це можливо. Він вводить поняття для розширення власного функціоналу:

  • "проміжні" типажі - використовуються для реалізації наскрізного функціоналу в обробці запитів;
  • модифікатори - використовуються для зміни запитів та відповідей найбільш ергономічним способом.

З базовою частиною модифікаторів та проміжних типажів ви познайомитеся під час статті.

Створення проекту

Для початку створимо проект за допомогою Cargo, використовуючи команду:

Скомпілювавши отримаємо відповідний виконуваний файл:

1 2 3 $ rustc hello.rs $ du -h hello 632K hello

632 кілобайт для простого принта? Rust позиціонується як системна мова, яка має потенціал для заміни C/C++, чи не так? То чому б не перевірити аналогічну програму на найближчому конкуренті?

У нашому середовищі широко поширена думка про те, що однією з переваг збирача сміття є простота розробки високопродуктивних lock-free структур даних. Ручне управління пам'яттю в них зробити не просто, а GC легко вирішує цю проблему.

Цей пост покаже, що, використовуючи Rust, можна побудувати API управління пам'яттю для конкурентних структур даних, яке:

  • Уможливить реалізацію lock-free структури даних, як це робить GC;
  • Створить статичну захист від неправильного використання схеми керування пам'яттю;
  • Буде мати порівняні з GC накладні витрати (і більш передбачувані).

У тестах, які я покажу нижче, Rust легко перевершує реалізації lock-free черг Java, а саму реалізацію на Rust легко написати.

Я реалізував схему управління пам'яттю, засновану на епохах («epoch-based memory reclamation») у новій бібліотеці Crossbeam, яка на сьогоднішній день готова до використання з вашими структурами даних. У цьому пості я розповім про lock-free структури даних, алгоритм епох і внутрішній API Rust.

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

Простіше кажучи: порушення доступу до пам'яті – це якісь дії з некоректними даними, а витік пам'яті – це відсутністьпевних дій із коректними даними. У табличній формі:

Я маю кілька думок про вивчення мов програмування.

По-перше, ми підходимо до цього неправильно. Я впевнений, що ви відчували такі самі відчуття. Ви намагаєтеся вивчити нову мову і не зовсім розумієте, як у ній все влаштовано. Чому в одному місці використовується один синтаксис, а в іншому? Всі ці диваки дратують, і в результаті ми повертаємося до звичної мови.

Я вважаю, що наше сприйняття мов грає з нами злий жарт. Згадайте, як ви останній раз обговорювали нову мову. Хтось згадав про нього, а хтось інший поцікавився його швидкістю, синтаксисом або наявним веб-фреймворком.

Це дуже схоже на обговорення автомобілів. Чи чули про новий УАЗ Рибак? Наскільки він швидкий? Чи можу я проїхати на ньому через озеро?

Коли ми схожим чином говоримо про мови, то маємо на увазі, що вони взаємозамінні. Як машини. Якщо я знаю, як керувати Ладою Саранськ, то зможу вести і УАЗ Рибак без будь-яких проблем. Різниця тільки в швидкості та приладовій панелі, чи не так?

Але уявіть, як виглядатиме PHP-автомобіль. А тепер уявіть, наскільки відрізнятиметься автомобіль Lisp. Пересісти з одного на інший вимагатиме набагато більшого, ніж засвоїти, яка кнопка керує опаленням.

Примітка: Ця стаття передбачає, що читач знайомий з Rust FFI (переклад), порядком байтів (endianess) та ioctl.

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

Отже, хочемо представити вашій увазі нещодавнього іменинника (15 травня 2016 року йому виповнився рік) — Rust. Це універсальна мова програмування, яку розробляє компанія Mozilla, три основні принципи якої: швидкість, безпека та ергономіка. Самі автори нескромно вважають його одним з найбільш ймовірних спадкоємців C/C++. Згідно з опитуванням порталу StackOverflow, саме Rust сьогодні найбільш улюблена розробниками мова. Отже, давайте докладніше розбиратися в тому, що ж він являє собою.

Rust для новачка

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

Втім, Rust швидко розвивається (кожні 6 тижнів виходить новий реліз), спільнота зростає, а знайти інформацію в інтернеті вже не складає ніяких труднощів.

Як вивчати

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

Для тих, хто пройшов початкову стадію знайомства, на GiHub можна знайти безліч корисного матеріалу, у тому числі RFC і комміти. Крім того, ви можете відвідати особисто або хоча б переглянути інтернет-трансляцію однієї з конференцій з Rust, намічених на другу половину року. Ось календар:

  • 9-10 вересня конференція RustConf у Портленді, США;
  • 17 вересня конференція європейської спільноти RustFest у Берліні, Німеччина;
  • 27 жовтня конференція Rust Belt Rust у Піттсбурзі, США;

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

Особливості

Трохи дублюючи те, що було сказано раніше, виділимо основні плюси і мінуси мови Rust.

Плюси:

  • Безпечна робота з пам'яттю;
  • Висока швидкодія;
  • Алгебраїчний тип даних;
  • Передбачуваність компіляції;

Мінуси:

  • Певна надмірність коду;
  • Висока інтенсивність розвитку мови та, як наслідок, відсутність гарної актуальної літератури для вивчення;
  • Необхідність чітко та однозначно прописувати параметри для компіляції.

Насправді до відмінностей, на кшталт заміни наслідування на здібності, швидко звикаєш. Як тільки очі звикли, руки набилися, Rust перетворюється на цілком робочу мову, простіше і функціональніше C++, але поступається по «красивості» багатьом іншим мовам програмування. Фактично ж головна відмінність Rust від конкурентів та попередників – саме швидкість та безпека.

Затребуваність

На сьогоднішній день Rust популярний серед розробників ігор, графіки та операційних систем. Однак зі зрозумілих причин кількість стаціонарних місць, де потрібні були вузькопрофільні знавці Rust, у світі і вже тим більше в Росії вкрай невелика. Тим не менш, поки не видно жодних передумов, що мова кане в лету, більше схоже на планомірне захоплення світу. А отже, хороші навички використання Rust у майбутньому допоможуть знайти високооплачувану цікаву роботу як у нашій країні, так і за кордоном.

Ще затребуваного: професія «Статті».

Rust розвивається стабільно, нові можливості та виправлення вводяться з кожним релізом раз на 6 тижнів. Помічені баги також виправляються оперативно у нерегулярних мінорних релізах. Іноді така динаміка розвитку навіть може бути перешкодою: багато "живих" бібліотек потребують нової версії компілятора, але не кожна компанія здатна швидко оновлювати його на своїх проектах.

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

Якогось єдиного збірника кращих практик використання Rust, наскільки я знаю, поки що немає. Багато корисних порад є в офіційній документації (у так званих Книгах), а також розкидано за різними окремими статтями. Проте, є списки корисних статей, які допоможуть знайти серед них потрібну. Наприклад, ці:
https://github.com/ctjhoa/rust-learning
https://github.com/brson/rust-anthology/blob/maste...

У нових проектах Rust використовується, і поки що тенденція йде на розширення. Ось на цій сторінці ви можете подивитися, які компанії використовують Rust зараз і для чого: https://www.rust-lang.org/en-US/friends.html

Отже, якщо ви плануєте використовувати Rust у виробництві, готуйтеся до чого:

  1. Досить високий поріг входу до язика. Тут немає особливої ​​складності, просто знадобиться практика мовою і спочатку час на дотримання порад компілятора з усунення помилок компіляції, що постійно виникають.
  2. Досить часті оновлення компілятора щодо додавання нових можливостей у мову. Це може призвести до того, що потрібна вам бібліотека вимагатиме свіжу версію компілятора.
  3. Сироваті бібліотеки. Ймовірно, вам доведеться їх трохи доопрацьовувати під себе.
  4. Rust полегшує складне, але ускладнює просте. Для дуже простих проектів, що не потребують високої продуктивності та серйозних доробок у майбутньому, можливо, Rust буде не найкращим вибором.
Але що ви отримаєте від використання Rust?
  1. Висока продуктивність програм, автоматичне управління пам'яттю без збирача сміття.
  2. Високу надійність та захищеність програм, усунення великої кількості потенційних проблем на етапі компіляції.
  3. Досить легкий та безпечний процес рефакторингу та доопрацювання програм завдяки розвиненій системі типів.
  4. Розвинуту систему керування залежностями проекту.
  5. Дійсно хороший універсальний інструмент: Rust підійде і для прототипування, і для розробки, причому для будь-якого типу програм (утиліти, настільні програми, веб-додатки, мобільні програми, системи, що вбудовуються). Хороша підтримка поки що є не для всього, але на перспективу це великий плюс.

У 2013 році компанія Mozilla спільно з Samsung повідомила про створення нового механізму веб-браузера Servo. Він створювався спеціально для багатоядерних процесорів мобільних пристроїв, здатний розбивати завдання на паралельні потоки та зменшувати час завантаження веб-сторінок. Servo повністю написаний мовою програмування Rust, яку Mozilla розробила сама для написання мобільних додатків.

Про мову

Rust – процедурна мова програмування, що підтримує різні стилі написання коду. Розробник Грейдон Хор почав створювати мову в 2006 році, і за три роки до проекту підключилася Mozilla. 2010 року Rust презентували на конференції Mozilla Summit. У цьому року розробку перевели на компілятор, написаний на Rust. Компілятор використовував універсальну систему аналізу та трансформації програм LLVM як базу даних.

Перша стабільна версія мови вийшла у 2015 році. Після релізу альфа-версії Rust зазнав правок - усередині компілятора залишили тільки готові можливості, які не змінюватимуться. Решту перенесли в експериментальний розділ.

В основі мови Грейдон Хор заклав такі поняття як:

  • Безпека. Rust містить низку обмежень для програміста, які включені за замовчуванням. Для їх відключення в блоках та функціях потрібна мітка unsafe.
  • Швидкість. Мова можна порівняти за швидкістю з іншою мовою програмування C++, що дає явний ряд переваг для програміста.
  • Паралелізм. Система може виконувати кілька обчислень одночасно, разом із цим можуть взаємодіяти друг з одним.
  • Короткість. Перші ключові слова в Rust укладалися п'ять символів. Але згодом це обмеження зняли.

Приклад одного з перших кодів на Rust

Однак Rust не позбавлений і мінусів, найяскравіші з них:

  • Надмірність коду.
  • Відсутність літератури вивчення мови.
  • Чіткість у внесенні параметрів компіляції. Це не завжди влаштовує досвідчених програмістів, тому що в інших мовах таких правил немає.

Втім, мова регулярно модернізується та доповнюється: її оновлення виходять раз на 6 тижнів.

Порівняння Rust із C++

Творці Rust вважають його спадкоємцем C++, який виник на початку 1980-х років, коли розробник придумав кілька удосконалень до мови С. Тому варто порівняти молоду мову з перевіреним часом.

  • Звертання до віддаленої пам'яті. У C++ через видалення змінної може виникнути низка проблем. Подібні ускладнення неможливі в Rust, оскільки у ньому немає команд видалення пам'яті. Компілятор спадкоємця повідомить, що код містить помилку, а компілятор С++ виведе результат без віддалених значень, навіть не повідомивши про проблему.
  • Крапка з комою. Додавши в код зайву точку з комою, ви викличете помилку в С++, тоді як у Rust тіло циклу полягає у фігурні дужки.
  • Небезпечний код. У Rust є мітка unsafe, яка ізолює основний код від небезпечного. У майбутньому, під час перегляду коду це дозволяє звузити пошук вразливостей.

Саме на C++ був реалізований Firefox: ця примхлива мова вимагала підвищеної уваги до деталей. Інакше помилки оберталися серйозними вразливістю. Rust був покликаний упоратися з цією проблемою.

Перспективи

У рейтингу RedMonk за третій квартал 2018 мова програмування від Mozilla стабільно посідає 23 місце. Експерти вважають, що покращення позицій йому не загрожує. Незважаючи на це, у серпні 2018 року творці випустили оновлений Rust 1.28.

Після релізу Rust у 2015 році, за даними майданчика Stack Overflow, з ним хотіли ознайомитись 74% розробників. Проте вже у 2016 році він перемістився на перше місце: 79% користувачів назвали Rust улюбленою мовою програмування та виявили бажання продовжити роботу з ним. Перше місце за цим параметром Rust посів і 2018 року.

Stack Overflow – популярна система питань та відповідей про програмування, розроблена у 2008 році.

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

© 2023 androidas.ru - Все про Android