Чому Zig, коли вже є C++, D і Rust?
Немає прихованого потоку керування
Якщо Zig-код не виглядає так, ніби він відходить кудись, щоб викликати функцію, то він цього і не робить. Це означає, що ви можете бути впевнені, що наступний код викликає лише foo()
, а потім bar()
, і це гарантовано без необхідності знати типи чогось:
var a = b + c.d;
foo();
bar();
Приклади прихованого потоку керування:
- У D є функції
@property
, тобто методи, які ви викликаєте за допомогою того, що виглядає як доступ до поля, тому в наведеному вище прикладіc.d
може викликати функцію. - C++, D і Rust мають перевантаження операторів, тому оператор
+
може викликати функцію. - C++, D і Go мають винятки throw/catch, тому
foo()
може викликати виняток і запобігти викликуbar()
. (Звичайно, навіть у Zigfoo()
може заблокуватись і запобігти викликуbar()
, але це може статися в будь-якій мові, повній Тьюрінга.)
Метою цього дизайнерського рішення є покращення читабельності.
Жодних прихованих алокацій
Zig має вільний підхід, коли справа доходить до розподілу купи. Немає ключового слова new
або будь-яка інша функція мови, яка використовує розподільник купи (наприклад, оператор конкатенації рядків[1]). Вся концепція купи керується бібліотекою та кодом програми, а не мовою.
Приклади прихованих алокацій:
defer
Go виділяє пам'ять для локального стека функції. Окрім того, що це неінтуїтивний спосіб керування роботою цього потоку, це може спричинити нестачу пам’яті, якщо ви використовуєтеdefer
всередині циклу.- Співпрограми C++ виділяють пам’ять купи для виклику співпрограми.
- У Go виклик функції може спричинити розподіл купи, оскільки goroutines виділяють невеликі стеки які змінюються, коли стек викликів стає достатньо глибоким.
- Основні API стандартної бібліотеки Rust панікують через брак пам’яті та альтернативні API, які приймають параметри розподільника, є запізнілою думкою (див. rust-lang/rust#29802).
Майже всі мови, що зі збирачем сміття, мають розкидані приховані виділення, так як збирач сміття приховує докази на стороні очищення.
Основна проблема з прихованими розподілами полягає в тому, що вони перешкоджають повторному використанню фрагменту коду, без потреби обмежуючи кількість середовищ, у яких цей код буде підходить для розгортання. Простіше кажучи, є випадки використання, коли потрібно вміти покладатися на потік керування та виклики функцій, щоб не мати побічного ефекту розподілу пам’яті, тому мова програмування може обслуговувати ці випадки використання, лише якщо це реально дає цю гарантію.
У Zig є стандартні бібліотечні функції, які забезпечують і працюють із розподільниками купи, але це додаткові функції стандартної бібліотеки, не вбудовані в саму мову. Якщо ви ніколи не ініціалізуєте розподільник купи, ви можете бути впевнені, що ваша програма не буде розподіляти купу.
Кожна функція стандартної бібліотеки, яка потребує виділення пам’яті купи, приймає параметр Allocator
щоб це зробити. Це означає, що стандартна бібліотека Zig підтримує окремі цілі. Наприклад, std.ArrayList
і std.AutoHashMap
можна використовувати для програмування на голому металі!
Користувальницькі розподільники спрощують керування пам’яттю вручну. Zig має розподільник налагодження, який забезпечує безпеку роботи з пам'яттю в умовах використання після звільнення та подвійного звільнення. Це автоматично виявляє та друкує стек-трейс витоків пам'яті. Існує розподільник арен, щоб ви могли об’єднувати будь-яку кількість алокацій в одну й звільняйте їх усі одразу, а не керувати кожним розподілом окремо. Розподільники спеціального призначення можна використовувати для підвищення швидкодії або використання пам’яті для потреб будь-якої конкретної програми.
[1]: Насправді існує оператор конкатенації рядків (зазвичай оператор конкатенації масиву), але він працює лише під час компіляції, тому він все ще не виконує жодного розподілу купи під час виконання.
Першокласна підтримка без стандартної бібліотеки
Як було зазначено вище, Zig має абсолютно необов’язкову стандартну бібліотеку. Кожен API std lib компілюється у вашу програму, лише якщо ви його використовуєте. Zig однаково підтримує зв’язування з libc або відсутність зв’язування з нею. Zig дружній до голих металів і високопродуктивних розробок.
Це найкраще з обох світів; наприклад, у Zig програми WebAssembly можуть як використовувати звичайні функції стандартної бібліотеки, так і створювати найдрібніші двійкові файли порівняно з іншими мовами програмування, які підтримують компіляцію до WebAssembly.
Портативна мова для бібліотек
Одним із святих граалів програмування є повторне використання коду. На жаль, на практиці ми багато разів знову винаходимо колесо. Часто це виправдано.
- Якщо програма має вимоги до реального часу, тоді будь-яка бібліотека, яка використовує збирання сміття або будь-яку іншу недетерміновану поведінку, дискваліфікується як залежна.
- Якщо мова робить надто легким ігнорування помилок і, отже, важко перевірити, чи бібліотека правильно обробляє та видає помилки, може виникнути спокуса проігнорувати бібліотеку та реалізувати її повторно, знаючи, що одна обробила всі відповідні помилки правильно. Zig розроблено таким чином, що найлінивіша річ, яку може зробити програміст, — це правильно обробляти помилки, і тому можна бути достатньо впевненим, що бібліотека належним чином видалятиме помилки.
- Наразі це прагматично вірно, що C є найбільш універсальною та портативною мовою. Будь-яка мова, яка не має можливості взаємодіяти з кодом C, ризикує бути невідомою. Zig намагається стати новою портативною мовою для бібліотек, одночасно спрощуючи відповідність C ABI для зовнішніх функцій і запроваджуючи безпеку та мовний дизайн, який запобігає поширеним помилкам у реалізаціях.
Менеджер пакетів і система збирання для існуючих проектів
Zig — це інструментальний ланцюжок на додаток до мови програмування. Він поставляється з системою збирання та менеджером пакетів, які корисні навіть у контексті традиційного проекту C/C++.
Ви можете не лише писати код Zig замість коду C або C++, але й використовувати Zig як заміну autotools, cmake, make, scons, ninja тощо. Крім того, він надає менеджер пакунків для власних залежностей. Ця система збірки підходить, навіть якщо вся кодова база проекту написана на C або C++. Наприклад, портування ffmpeg до системи збірки zig, стало можливим скомпілювати ffmpeg на будь-якій підтримуваній системі для будь-якої підтримуваної системи, використовуючи лише 50 МіБ завантаження zig. Для проектів з відкритим вихідним кодом ця спрощена можливість створювати з вихідного коду - і навіть крос-компіляція - може бути різницею між отриманням або втратою цінних учасників.
Системні менеджери пакетів, такі як apt-get, pacman, homebrew та інші, є інструментальними для кінцевого користувача, але їх може бути недостатньо для потреб розробників. Менеджер пакунків для певної мови може бути різницею між відсутністю учасників і багатьма. Для проектів з відкритим вихідним кодом складність створення проекту взагалі є величезною перешкодою для потенційних учасників. Для проектів C/C++ наявність залежностей може бути фатальною, особливо в Windows, де немає менеджера пакетів. Навіть коли лише створюють сам Zig, більшість потенційних учасників відчувають труднощі із залежністю LLVM. Zig пропонує спосіб для проектів залежати від нативних бібліотек напряму — без залежності від системного менеджера пакунків користувача, щоб мати доступну правильну версію, і таким чином, який практично гарантує успішне створення проектів з першої спроби, незалежно від системи. використовується й незалежно від того, на яку платформу націлено.
Інші мови мають менеджери пакетів, але вони не усувають надокучливу систему залежності, як це робить Zig.
Zig може замінити систему збірки проекту розумною мовою, використовуючи декларативний API для створення проектів, який також забезпечує керування пакетами, а отже, можливість фактично залежати від інших бібліотек C. Можливість мати залежності забезпечує абстракції вищого рівня, а отже, поширення коду високого рівня, який можна багаторазово використовувати.
Простота
C++, Rust і D мають таку велику кількість функцій, що вони можуть відволікати від реального значення програми, над якою ви працюєте. Замість того, щоб налагоджувати саму програму, людина налагоджує свої знання мови програмування.
Zig не має макросів, але все ще достатньо потужний, щоб виражати складні програми зрозумілим способом, що не повторюється. Навіть у Rust є макроси з особливими випадками, наприклад format!
, який реалізований в самому компіляторі. Тим часом у Zig еквівалентна функція реалізована в стандартній бібліотеці без коду спеціального випадку в компіляторі.
Інструментарій
Zig можна завантажити з розділу завантажень. Zig надає двійкові архіви для Linux, Windows і macOS. Нижче описано, що ви отримуєте з одним із цих архівів:
- встановлюється шляхом завантаження та розпаковування одного архіву, конфігурація системи не потрібна
- статично скомпільований, тому немає залежностей під час виконання
- підтримує використання LLVM для оптимізованих збірок релізів і використання користувацьких серверних модулів Zig для швидшої компіляції
- додатково підтримує бекенд для виведення коду C
- готова крос-компіляція для більшості основних платформ
- постачається з вихідним кодом для libc, який буде динамічно скомпільовано, коли це необхідно для будь-якої підтримуваної платформи
- включає систему збірки з паралелізмом і кешуванням
- компілює код C і C++ із підтримкою libc
- сумісність командного рядка GCC/Clang із
zig cc
- компілятор ресурсів Windows