Інтернет Windows Android

Rust: починаємо працювати з новою мовою програмування від фахівців компанії Mozilla. Мова Rust і чому її треба з'їсти Rust з чого почати програмування


Нам дуже сподобалася стаття "Критика мови Rust і чому C/C++ ніколи не помре". Ми запропонували автору, що виконаємо переклад статті англійською мовою, а також опублікувати її у нашому блозі. Він погодився, і ми із задоволенням представляємо цю статтю російською та англійською мовами. Оригінал статті знаходиться.

Оригінал статті розміщено (текст російською мовою). Статтю опубліковано в нашому блозі з угоди автора.

Примітка: Нижче за текстом я виходжу з припущення, що Rust є спробою зробити швидку та безпечну мову. Зрештою, хлопці з Mozilla робили його як інструмент розробки браузерного двигуна. Якщо ж це чергова просто безпечна мова, тоді отримуємо дивну. Найрізноманітніших безпечних мов і так вже хоч греблю гати, кожен знайде собі до смаку. І якщо не варто мети замінити C++, то (1) навіщо в мові зроблено unsafe підмножина? (2) навіщо було видаляти з мови легковажні потоки, зручно ж? Іншими словами, у цьому випадку те, що відбувається, взагалі не має ніякого сенсу.

Якщо раптом ви почитаєте форум linux.org.ru, зазначу, це це не той список з 10 суто технічних причин не любити Rust, про який йшлося в цьому треді. Як показало обговорення у Skype з шановним товаришем @ sum3rman, є більше однієї думки щодо того, наскільки "технічними" вважати ці причини. Загалом, фіговий список я склав, але деякі пункти з нього, найцікавіші, мабуть, я все-таки ризикну навести. Насправді, тут і простих, не технічних причин за очі вистачає.

Те, що C/C++ в найближчому майбутньому нікуди не дінуться, і так будь-якій тверезо мислячій людині зрозуміло. Ніхто не буде переписувати багато десктопних додатків, ядра операційних систем, компілятори, ігрові та браузерні двигуни, віртуальні машини, бази даних, архіватори, аудіо- і відеокодеки, тонни інших сишних бібліотек, і так далі. Це дуже багато швидкого, налагодженого, перевіреного часом коду. Переписувати його дуже-дуже дорого, ризиковано, і якщо чесно, не позбавлене сенсу тільки в спотвореній свідомості тільки самих упокорених Rust'оманів. Попит на C/C++ програмістів був і буде ще дуже довго.

Добре, а як щодо застосування Rust при написанні нового коду?

Згадаймо, що це вже далеко не перша спроба зробити "правильніший" C/C++. Візьмемо хоча б мову D. З'явилася 2001 року, дуже хороша мова. Немає ні вакансій, ні нормальних інструментів розробки, ні якихось особливо визначних саксес сторіс. Проект OpenMW спочатку писали на D, а потім раптово вирішили повністю переписати на C++. Як зізнаються розробники, їм приходило багато листів у стилі "відмінний проект, ми були б раді в нього контриб'ютити, але не знаємо і не хочемо знати цей безглуздий D". Вікіпедія повідомляє, що крім D була і маса інших спроб в тій чи іншій мірі вбити C++, наприклад, Vala, Cyclone, Limbo, BitC. Чи багато хто взагалі чув про такі мови?

Думаю, давно настав час винести уроки з історії. Жодна розсудлива людина не потягне в проект нову мову, поки ви хоча б не покажете їй нормальні інструменти розробки, не розкажете пару саксес сторіс і не покажете десяток програмістів цією мовою, які живуть поблизу. Програмісти ж, мабуть, крім наймолодших, ніколи не будуть витрачати свій час і здоров'я на вивчення чергової правильної мови, поки ви не покажете їм нормальні інструменти розробки (не вироби типу Racer), пару десятків тисяч готових бібліотек (не "experimental", "unstable" і так далі), не розкажете пару саксес сторіс і не покажіть десяток відкритих вакансій у їхньому місті. Проблема курки та яйця. Дуже рідко цю проблему вдається успішно вирішити (умовно тут можна навести приклад і Scala), в основному завдяки вкладенню часу та грошей з боку деякої великої компанії (Google, Typesafe), з якихось міркувань зацікавлених у популяризації мови.

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

C/C++ критикують різне. Критикують, до речі, часто ті, хто в продакшені навіть здалеку не бачив коду на С++. Коротко і ясно проблему можна описати так: С + + дуже швидкий (а також не вимогливий до пам'яті, заряду батареї і т.д.), але не безпечний у тому сенсі, що дозволяє виходити за межі масивів, помилково звертатися до звільнених шматків пам'яті і так далі. У свій час ця проблема призвела до появи маси безпечних мов, таких як Java, C#, Python та інші. Але виявилося, що ці мови в порівнянні з C + + занадто вимогливі до ресурсів і мають інші недоліки, згадаємо хоча б неминучий stop the world при складанні сміття. Тому люди б'ються над завданням зробити мову такою ж швидкою, як C++, але ще й безпечною. Однією з таких мов є Rust.

Rust справді безпечний, але, на жаль, далеко не швидкий. За швидкістю на момент написання цих рядків Rust порівняємо з Java, Go та Haskell:

Я щиро сподіваюся, що згодом його якось розженуть, але доти в плані компромісу швидкості та безпеки він не набагато цікавіший за Scala чи Go. Досі залишається відкритим питання, чи можна взагалі зробити мову швидкою та безпечною, чи постійні перевірки на вихід за межі масиву, безпечні обв'язки навколо біндингу до сишних бібліотек і так далі автоматично роблять будь-яку мову вдвічі повільніше за С/C++.

А за рахунок чого, власне, Rust безпечний? Якщо говорити простими словами, це мова з вбудованим статичним аналізатором коду. Дійсно дуже крутим статичним аналізатором, який ловить всі типові для С++ помилки, причому пов'язані з управлінням пам'яттю, а й многопоточностью . Передав по каналу посилання на об'єкт, що змінюється, іншому потоку, а потім спробував скористатися цим посиланням сам - все, не скомпилиться. Це дійсно чудово.

Часто наводиться аргумент, що 90% часу виконується лише 10% коду (що, наскільки я розумію, суто емпіричне правило – швидко знайти суворих досліджень на цю тему не вдалося). Отже, більшу частину програми можна написати на безпечному Rust, а 10% "гарячого" коду - на його unsafe підмножині, і повільність поточної реалізації Rust насправді не є проблемою. Ок, але тоді виходить, що Rust взагалі не потрібний, тому що я можу написати 90% коду на Go, а 10% на Сі. Тільки шукачі срібних куль і відірвані від реальності те-єретики будуть використовувати Rust виключно з міркувань, що 100% програми можна написати як би однією мовою. Хоча насправді це два діалекти однієї мови, що не так сильно відрізняється від зв'язки Java плюс Сі або Go плюс Сі.

Насправді правило 10:90 - це все одно брехня. За цією логікою можна переписати 90% WebKit, 90% VirtualBox або 90% GCC на Java та отримати такий самий результат. Вочевидь це не так. Навіть якщо справа не в тому, що в ряді програм це відношення дуже інше, стежте за руками. Допустимо, вся програма написана на небезпечному C/C++ і час її виконання, умовно кажучи, дорівнює 0.9*1 (мала частина гарячого коду) + 0.1*1 (багато холодного коду) = 1. Тепер порівняємо з програмою безпечною мовою зі вставками на Сі: 0.9 * 1 + 0.1 * 2 = 1.1, щодо 10% різниці. Це багато чи мало? Залежить від масштабів. У випадку з Google навіть кілька відсотків можуть заощадити мільйони доларів (див. пункт 5 у пейпері, "Utilization"). Або уявіть, що з наступним оновленням JVM раптово вимагатиме на 10% більше ресурсів! Я боюся навіть гадати, скільки нулів буде у цифрі, отриманій після переказу відсотків на американські гроші! 10% - це дофіга у завданнях, де використовуються Сі та C++.

Ми повторюємо "передчасна оптимізація - корінь усіх лих", як мантру. Але якщо слідувати їй буквально, то давайте всюди використовувати бульбашкову сортування замість quicksort. Ми ж не знаємо точно, що програма саме тут гальмуватиме! Який сенс обертати звичайні лічильники якихось дій в актори або транзакційну пам'ять, якщо можна відразу скористатися ефективнішим atomic? І взагалі, у тривіальних випадках немає сенсу примусово ініціалізувати все-все-все змінні, робити купу додаткових перевірок тощо. Нехай ми отримаємо не 10% прискорення, а 2-5%. Адже це теж зовсім непогано, якщо вимагало всього лише кілька зайвих хвилин роздумів. І як ми вже з'ясували, у завданнях, які вирішуються на С/C++, це може бути великою різницею! Потім хто сказав, що знайти гаряче місце, переписати код (можливо, дуже багато коду) і довести, що він став дійсно швидше - це простіше, ніж подумати про продуктивність заздалегідь?

Якщо відволіктися від питання компромісу швидкості та безпеки, то щодо дизайну самої мови у мене теж є питання. Зокрема щодо п'яти типів покажчиків. З одного боку, це непогано, коли програміст замислюється над тим, де лежать змінні, в стеку чи купі, і можуть чи не можуть з ними одночасно працювати кілька потоків. Але з іншого, уявіть, що ви пишите програму, і виявилося, що змінна має жити не в стеку, а в купі. Ви переписуєте все, щоб використовується Box. Тому ви розумієте, що насправді потрібний Rc чи Arc. Знову переписуєте. А потім ще раз переписуєте на звичайну змінну у стеку. Все це – без нормальної IDE під рукою. І регулярки не допоможуть. Ну чи просто в стилі "Vec >>>", привіт, Java! Але що найсумніше, компілятор вже знає про час життя всіх змінних, він міг би виводити всі ці Box, Arc і так далі автоматично. Але чомусь ця частина роботи перекладена програмістом. Набагато зручніше було б просто писати val (у третьому тисячолітті!), а там, де треба, явно вказувати Box або Rc. Розробники Rust у цьому сенсі засмагали всю ідею.

Через це, зокрема, сильно звужується сфера застосування Rust. Ніхто в здоровому глузді не писатиме такою мовою веб і серверсайд. Особливо з огляду на те, що він не дає істотних переваг перед тими ж мовами під JVM. Та й Go з нормальними легковагими потоками (не футурами) для цих завдань виглядає набагато привабливіше. З футурами, щоб не прострелити собі ногу, потрібно ще навчитися працювати, а ви кажете "безпечну мову". Так, у цих мов свої особливості, взяти все той же stop the world, але ця проблема розв'язується, як розпилюванням на мікросервіси, так і іншими прийомами. І так, ніхто не транслюватиме Rust в JavaScript, писати на ньому скрипти для розкладки в AWS, або використовувати як мову запитів до MongoDB. Під Android теж навряд чи писати будуть, але з іншої причини - там дуже більше однієї архітектури, з JVM набагато простіше. Якщо ви раптом думали, що Rust "підходить для всіх завдань", змушений вас засмутити.

Ну і до купи:

  • Макроси як підпірка до зайвої багатослівності, викликаної відсутністю нормальних винятків. Я вже писав про проблеми метапрограмування, зокрема нормальну IDE для Rust ми навряд чи побачимо через нього. І я не впевнений, але схоже, що у макросів у Rust навіть неймспейсів немає.
  • Люди ідіоти, а cargo дуже заохочує стягування пакетів безпосередньо з git-репозиторіїв, в обхід Crates.io. У результаті велика ймовірність отримати такий самий бардак з пакетами, як і у світі Erlang з його Rabar"ом. До речі, у світі Go, схоже, така ж ситуація.
  • Як багато нових мов, Rust йде шляхом спрощення. Я загалом розумію, чому в ньому немає нормального наслідування та винятків, але сам факт, що хтось за мене вирішує такі речі, залишає неприємний осад. C++ не обмежує програміста в питаннях, чим користуватися, а чим ні.
  • Якщо вже йти шляхом спрощення, то викинути вже всі ці розширення мови. А то виходить, як у світі Haskell кожен програміст пише на своєму діалекті.
  • Смарт-інтери, якщо що, далеко не безкоштовні і не призводять до передбачуваного часу складання сміття. Якомусь потоку раптово випадає честь звільнити дуже глибоку структуру даних. Поки він ходить лабіринтом з мертвих посилань, потоки, що від нього залежать, терпляче туплять. Та сама проблема є і в Erlang з його маленькими купками, сам не раз спостерігав. Смарт поінтери мають і свої проблеми, ту саму фрагменатцію пам'яті та витоку. Забув вікпоінтер у цеклічній структурі, і все. І це в мові, яка претендує на безпеку. Якщо хочете передбачуваного часу GC, або вивчайте поведінку вашої програми під навантаженням, і вживайте заходів (згадаймо хоча б ті ж пули об'єктів), якщо час GC не влаштовує вас, або керуйте пам'яттю вручну.
  • Хтось бачив суворий опис семантики Rust? У нього хоч би memory model є? Теж мені "безпечна" мова, "що доводить коректність" програм, яка взагалі-то може трактувати вихідний код десятьма різними способами, ха!
  • Не можу вкотре не нагадати, що проблема майже завжди в людях, а не в технологіях. Якщо у вас виходить поганий код на C++ або Java раптом гальмує, це не тому, що технологія погана, а тому, що ви не навчилися правильно нею користуватися. Rust ви теж будете незадоволені, але вже з інших причин. Чи не простіше навчитися користуватися популярнішими інструментами та почати їх любити?

Загалом і в цілому, найближчі 5 років я краще інвестувати свій час у вивчення C/C++, ніж Rust. С++ - це промисловий стандарт. Цією мовою успішно вирішують найрізноманітніші завдання вже понад 30 років. А Rust і що з ним - незрозумілі іграшки з туманним майбутнім. Про швидку смерть С++ розмови йдуть як мінімум із 2000-х, але писати на C/C++ за цей час стали не меншими. Скоріше навпаки. І ми бачимо, що мова розвивається (C++11, C++14), для неї з'являються нові інструменти (згадаймо хоча б CLion та Clang), і відповідних вакансій просто купа.

Програміст на C++ завжди легко знайде собі роботу з більш ніж гідною зарплатою, а при необхідності швидко перевчиться на Rust. Назад дуже і дуже сумнівно. До речі, мова, якщо щось далеко не єдиний і не вирішальний фактор при виборі нового місця роботи. Крім того, досвідчений програміст на C/C++ легко встромляється у вихідники PostgreSQL або ядра Linux, використовує потужні сучасні інструменти розробки, а також має у своєму розпорядженні безліч книг і статей (скажімо, OpenGL).

Бережіть свій час та здоров'я, їх у вас не так багато, як здається!

У 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 організацій.



На сьогоднішній день синтаксис Rust підтримується в vim і emacs за допомогою синтаксичних файлів, що поставляються разом з компілятором.
Є також синтаксичні пакети для популярного пропрієтарного редактора Sublime Text 2 та вільного редактора Kate. Підтримки Rust в IDE поки що немає. Підтримка відладчиків, зважаючи на все, теж відсутня.

Разом з компілятором rustc постачаються такі утиліти:
> rustdoc- утиліта для автоматичної генерації документації з вихідного коду на кшталт Doxygen;
> rustpkg- менеджер пакетів, що дозволяє легко встановлювати додаткові пакети та бібліотеки;
> rusti- так звана REPL-утиліта (read-eval-print-loop). По суті це тестовий інтерпретатор, який приймає вираз на Rust з командного рядка, компілює його у внутрішнє уявлення LLVM, виконує та виводить результат;
> rust- Універсальна утиліта, що запускає інші утиліти або компілятор в залежності від параметрів. У мене вона так і не заробила.

Вся доступна документація мови зібрана на офіційному сайті www.rust-lang.org. Є детальний посібник (http://static.rust-lang.org/doc/tutorial.html) - вичерпна формальна документація по всіх нюансах синтаксису, моделі пам'яті, системі часу виконання тощо, а також документація по вбудованій бібліотеці core та стандартної бібліотеки std. Уся документація англомовна. Російською мовою актуальних матеріалів немає, а пара оглядових статей вже встигли сильно застаріти.

Ідеологія та синтаксис


Rust відноситься до Сі-подібних мов, що використовують фігурні дужки для виділення блоків коду. Мова є «мультипарадигменним», тобто. дозволяє писати код в імперативно-процедурній, об'єктно-орієнтованій, конкурентній чи функціональній манері. Rust компілюється в нативний бінарний код на будь-якій підтримуваній платформі (використовує LLVM як бекенд). Теоретично код на Rust ні поступатися швидкості коду на C/C++. Rust позиціонується як системна мова, проте в ній немає вбудованої підтримки блоків коду на асемблері як у «справжніх» системних мовах С, С++ чи D.

Модель пам'яті Rust спочатку не допускає появи нульових або «висячих» покажчиків та переповнень буфера. Є опціональний збирач сміття, що працює лише в межах однієї нитки коду. У мови є вбудована підтримка легковажної багатозадачності та комунікацій між нитками за допомогою обміну повідомленнями. Паділення пам'яті (shared memory) в Rust не існує в принципі. Всі змінні поділяються на стікові, змінні купи для цього потоку, і змінні так званої «обмінної» купи, які можуть читатися всіма потоками, але не можуть ними змінюватися. Це автоматично виключає «заклинювання» (deadlock), яке вважається бичем багатопотокового програмування. ABI мови сумісний із Сі, тому програми на Rust можуть компонуватися з бібліотеками, написаними на Сі без додаткових обгорток. Для потреб низькорівневого системного програмування та забезпечення сумісності з Сі у мові є особливий «небезпечний» режим без перевірки коректності покажчиків. За своєю ідеологією Rust найближче до мови Go. Так само, як і в Go, основний акцент зроблений на простоті багатопоточного програмування та швидкості розробки масштабних додатків, а синтаксис місцями так само незвичний і в чомусь дивовижний. У той же час, Rust не настільки мінімалістичний, як Go, і претендує на роль системної мови.

Синтаксис Rust переважно запозичений із З і З++ з домішкою ідей з мов Go, C#, Haskell, Python і Ruby. Не вичерпно описуватиму синтаксис мови, а зупинюся тільки на найцікавіших концепціях.

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 підтримуються спеціальні «розумні» покажчики з вбудованим підрахунком посилань - унікальні (Unique boxes), що розділяються (Shared 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 відсутня вбудована підтримка об'єднань, тому нам доведеться виробити стратегію самостійно. З об'єднання - це тип, який зберігає різні типи даних в одній області пам'яті. Існує багато причин, з яких можна віддати перевагу об'єднанню, такі як: перетворення між бінарними уявленнями цілих чисел і чисел з плаваючою точкою, реалізація псевдо-поліморфізму та прямий доступ до біт. Я сфокусуюсь на псевдо-поліморфізмі.