Rust пытается отобрать у C роль универсального языка между языками

13689
Rust пытается отобрать у C роль универсального языка между языками

Разработчик Valen нашёл способ обойти главный барьер C ABI.

image

C десятилетиями служит общим знаменателем для языков программирования, которым нужно обмениваться функциями и данными. Создатель экспериментального языка Valen Эван Овадия решил обойти привычную схему и напрямую связать два компилятора. Проект Golden Spike уже позволяет коду Valen обращаться к обобщённым функциям Rust, а Rust в ответ может вызывать код Valen.

Обычный путь между низкоуровневыми языками проходит через C ABI, набор соглашений о передаче аргументов, размещении данных и вызове функций. Rust поддерживает подобное взаимодействие через extern "C" и совместимое представление типов. Причина популярности подхода проста: собственный ABI Rust не даёт гарантий стабильности, тогда как C ABI давно поддерживается множеством компиляторов и платформ.

Универсальность C имеет цену. В языке нет дженериков в том виде, в котором их использует Rust, поэтому через обычный C ABI нельзя напрямую передать обобщённую функцию вроде работающей с произвольным типом T. Разработчикам приходится писать обёртки, заранее определять конкретные типы и сводить более богатые конструкции языка к набору, который понимает C.

Овадия уже пытался решить проблему в предыдущем языке Vale. Версия 2024 года запускала rustdoc, разбирала полученный JSON, отдельно выясняла размеры структур через небольшие программы на Rust, а затем генерировала промежуточную библиотеку с C-обёртками. Схема позволяла использовать часть обобщённого кода, но требовала заранее указывать типы и не позволяла, например, заставить тип из другого языка полноценно реализовать Rust trait.

Golden Spike меняет сам принцип взаимодействия. Компилятор Valen запускает rustc внутри собственного процесса через rustc_driver. Механизм позволяет загружать зависимости Rust и выполнять собственный код между отдельными стадиями компиляции. Valen благодаря такому доступу может задавать rustc вопросы о типах и функциях используемых библиотек вместо попытки заранее перевести сложный интерфейс на язык C.

Одного доступа к rustc оказалось недостаточно. Valen использует собственное промежуточное представление и собственный бэкенд, поскольку MIR, внутреннее представление Rust, не умеет выразить некоторые нужные новому языку возможности. Среди примеров Овадия называет групповое заимствование, поддержку определённых LLVM alias scopes и запланированное метапрограммирование времени компиляции.

Для совместной работы двух компиляторов разработчик добавил в rustc два дополнительных перехвата. Когда Rust начинает мономорфизацию заготовки функции Valen, управление временно получает компилятор Valen, который создаёт нужную специализированную версию и сообщает rustc, какие функции Rust понадобятся дальше. Перед генерацией LLVM-кода происходит похожий обмен: вместо стандартного бэкенда Rust машинное представление нужного фрагмента создаёт бэкенд Valen.

Изменения в Rust заняли около 100 строк, но Овадия прямо называет нынешнюю реализацию экспериментальной. Один из перехватов предполагает использование LLVM, хотя rustc поддерживает и другие бэкенды, включая GCC и Cranelift. Автор считает текущий патч непригодным для включения в основной код Rust и уже планирует переделать механизм.

Проверкой концепции стала графическая программа. Valen смог обратиться к библиотеке Rust на базе wgpu, передать управление в Rust, а затем получить обратный вызов в Valen через замыкание. Такой двусторонний сценарий особенно важен для событийных циклов графических приложений, где одной возможности вызвать функцию Rust из другого языка недостаточно.

До полноценной замены C ABI ещё далеко. Работа над безопасностью памяти на границе двух языков продолжается. Valen уже проверяет заимствования при переходах между Valen и Rust, однако часть возможностей остаётся ограниченной. Структуры Valen могут реализовывать traits Rust, но в основной реализации пока должны иметь нулевой размер, а поддержка структур с полями существует только в отдельном прототипе. Генерационные ссылки временно отключены, а групповое заимствование пока не охватывает замыкания.

Сам Valen также находится на экспериментальной стадии. Golden Spike пока не превращает Rust в новый универсальный ABI и не отменяет десятилетия инфраструктуры вокруг C. Эксперимент показывает другой возможный путь: вместо сведения нескольких языков к возможностям C компиляторы могут взаимодействовать напрямую и сохранять дженерики, traits и более сложную информацию о типах. Для новых языков подобная схема открывает особенно заманчивую возможность пользоваться экосистемой Rust, не создавая собственный набор библиотек с нуля.