C служит промежуточным слоем между языками программирования уже десятилетия. Разработчик Evan Ovadia создал экспериментальный язык Valen и решил отойти от этой схемы. Его проект Golden Spike напрямую связывает компиляторы: код Valen вызывает обобщённые функции Rust, а Rust в ответ может запускать код Valen.
Обычная связь между низкоуровневыми языками идёт через C ABI — набор правил о передаче аргументов, размещении данных и вызове функций. Rust поддерживает такое взаимодействие через extern 'C' и совместимые типы. Причина простая: собственный ABI Rust не даёт гарантий стабильности, тогда как C ABI давно стандартизирован и поддерживается множеством платформ.
Универсальность C имеет недостатки. В языке нет дженериков, поэтому через C ABI нельзя напрямую передать обобщённую функцию, работающую с произвольным типом. Разработчикам приходится писать обёртки, заранее фиксировать конкретные типы и упрощать конструкции языка до того, что понимает C.
Ovadia пытался решить эту проблему в предыдущем языке Vale. В версии 2024 года он запускал rustdoc, разбирал JSON, отдельно выясняя размеры структур через программы на Rust, а потом генерировал C-обёртки. Метод позволял использовать часть обобщённого кода, но требовал заранее указывать типы и не давал типам из других языков полноценно реализовывать Rust traits.
Golden Spike меняет принцип. Компилятор Valen запускает rustc внутри собственного процесса через rustc_driver. Это позволяет Valen загружать Rust-зависимости и выполнять свой код между стадиями компиляции. Вместо попытки заранее переводить сложный интерфейс на C, Valen может напрямую спросить rustc о типах и функциях используемых библиотек.
Одного доступа к rustc оказалось недостаточно. Valen использует собственное промежуточное представление и бэкенд, потому что MIR (внутреннее представление Rust) не выражает нужные возможности. Ovadia называет групповое заимствование, поддержку определённых LLVM alias scopes и запланированное метапрограммирование во время компиляции.
Для совместной работы двух компиляторов разработчик добавил в rustc два перехвата. Когда Rust начинает мономорфизацию заготовки функции Valen, управление временно получает компилятор Valen — он создаёт нужную специализированную версию и говорит rustc, какие функции понадобятся дальше. Перед генерацией LLVM-кода происходит похожий обмен: бэкенд Valen создаёт машинное представление вместо стандартного бэкенда Rust.
Изменения в Rust занимают около 100 строк кода, но Ovadia называет реализацию экспериментальной. Один из перехватов предполагает использование LLVM, хотя rustc поддерживает и другие бэкенды — GCC, Cranelift. Автор считает текущий патч непригодным для включения в основной код Rust и уже планирует переделку механизма.
Идею проверили на графической программе. Valen обратился к Rust-библиотеке на базе wgpu, передал управление в Rust, а потом получил обратный вызов в Valen через замыкание. Такая двусторонняя работа особенно нужна для событийных циклов графических приложений, где одного вызова Rust из другого языка недостаточно.
До замены C ABI ещё далеко. Работа над безопасностью памяти на границе двух языков продолжается. Valen проверяет заимствования при переходах между языками, но часть возможностей остаётся ограниченной. Структуры Valen могут реализовывать traits Rust, но в текущей версии только нулевого размера. Поддержка структур с полями существует только в отдельном прототипе. Генерационные ссылки отключены, групповое заимствование не охватывает замыкания.
Сам Valen тоже находится на экспериментальной стадии. Golden Spike пока не делает Rust новым универсальным ABI и не отменяет десятилетия инфраструктуры вокруг C. Эксперимент показывает другой путь: вместо сведения нескольких языков к возможностям C компиляторы могут взаимодействовать напрямую и сохранять дженерики, traits и сложную информацию о типах. Для новых языков это открывает возможность пользоваться экосистемой Rust, не создавая собственные библиотеки с нуля.
