FFI и интеграция в другие языки: мегатред


#1

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

Начну и предлагаю всем делиться опытом использования Rust из других языков и других языков из Rust.

У меня есть проект, который реализует движок исполнения скриптов на Lua, и предоставляет скриптам API для управления несколькими нативными объектами: виртуальными и физическими машинами (через единый API) и процессами на данной машине. Для примера, с машинами можно делать:

  • запускать данный образ ОС на них
  • включать/выключать/перезагружать
  • получать вывод из последовательного порта
  • писать в последовательный порт
  • над выводом можно делать дальнейшие действия, типа сравнения с образцом (простым или с использованием регвыров)

По сути это всё похоже на операционную систему в миниатюре. Когда в скрипте написано

  machine = Machine.new(config)

Rust создаёт реальную структуру Machine, в которой лежит вся необходимая для управления информация, а в Lua отдаёт хэндл - просто индекс в массив машин на стороне Rust.

Для всего этого я пользуюсь hlua.

Что нравится:

  • ядро на Rust - безопасно, удобно, легко расширять (например сериализовать результат выполнения скрипта в JSON);

Что не нравится:

  • hlua - довольно сырая штука, и там помимо багов встречаются полностью нереализованные возможности; некоторые фиксы заблокированы продвинутыми возможностями компилятора. На данный момент она не поддерживается и очень напрашивается форк.

Подробнее можно посмотреть в слайдах к прошлогоднему докладу.


#2

А ты не думал взять какой ни будь скриптовый язык раста для этого дела? Коих в принципе прилично (список)

Сам только раст с js дружил. Кроме проблем с enum-ами ни чего больше и не помню. Но они в принципе везде возникают, при работе с FFI


#3

Мы стараемся подружить Rust с Java: https://github.com/exonum/exonum-java-binding

На данный момент используем библиотеку jni-rs, которая предоставляет Rust-байндинги для довольно известного Java Native Interface. Написание кода для связи двух языков выглядит довольно прямолинейно - в Java достаточно объявить нативный метод, в Rust - его реализовать с правильной сигнатурой.

Основные проблемы - с передачей сложных структур данных между языками и с обработками исключений, которые могут возникать как с одной, так и с другой стороны.
Впрочем, работает все довольно неплохо и стабильно.

Есть и другие проекты по взаимодействию Rust <-> Java: JNR, JNA и перспективный GraalVM.

Мы уже не раз рассказывали про проект на различных конференциях: раз, два, три


#4

Не, не хочу кроме разработки проекта заниматься разработкой языка. Lua - проверенный вариант, который все хвалят за встраиваемость.


#5

Структуры бинарно передаёте или сериализуете?
А с исключениями что делаете? Кажется размотка стека через границу FFI это UB.


#6

Но твой вариант тоже не сказать что стабильный, как ты пишешь. Да и потом вроде у тебя не такие сложные задачи требуются от скрипта решать. Хотя могу ошибаться. Я бы глянул от задач, если хватает, то может все таки стоит выбрать скриптовый язык который более удобно с растом конектится, да и вроде как развитие есть.


#7

уже несколько месяцев как вступил на “скользкую” дорожку раста… и не жалею.
Хотя несколько раз пребывал в таком тупике, что думал - а может ну его нафиг.

В основном работаю над сопряжением работы ИЗ Rust с базовой библиотекой разбора протокола написанной на С.

Непонятки были с тем что генерил bindgen для undefined size array in struct
ну и поддержка сишных Variadic functions вызывало вопросы и озабоченность.

Rust подкинул мне несколько идей.
В частности, думал и решил - полностью перехожу на строки в формате UTF8 . Это позволит отказаться от необходимости при описании протокола указывать сколько байт занимает один char данной строки.
Да, вычисление количества символов в строке будет не таким простым, но мой анализ показывает, что чаще всего длинна строки была нужна, и использовалась, чтобы отвести под её хранение, перемещение необходимое число байт.

Вот этот разрыв в мозгу, прямой зависимости между числом символов в строке и числом байт, потребовал усилий…

  • архитектуру сишного кода в некоторых аспектах переосмыслил.

А вообще команда у раста просто отличная. Страшно доволен языком и языковыми конструкциями.
Раньше подумывал после версии для Rust буду пилить версию для C++ … теперь однозначно решил, что C++ версии не будет.
атавизм ибо.


#8

Вариант стабильный, т.е. он не меняется спонтанно.

Это известный язык с обширной документацией, и заведомо известно что сам язык достаточно матёрый, чтобы не иметь проблем с самим интерпретатором.

Другой скриптовый язык, который на коленке делают в опенсорсе, будет также подвержен проблеме что его не поддерживают - только не поддерживают уже не только FFI-обёртку, а интерпретатор в целом.

В конечном счёте пользуюсь этим не я один и Lua - это понятный выбор, а никому не известный скриптовый язык на расте - нет.


#9

согласен. LUA отличный выбор.
умудрился на нем под OpenWrt HTTP tunnel написать.
работал даже на слабеньких роутерах.


#10

Я столкнувшись с подобной проблемой написал https://github.com/Dushistov/rust_swig .
Так как во всех трех вариантах JNI, JNR и JNA нужно писать много почти одинакового кода,
причем в самом эффективном варианте, то есть JNI приходится еще много почти одинакового unsafe кода писать.


#11

Каюсь, все никак не находил время на ответ.

Структуры бинарно передаёте или сериализуете?

Поступаем просто - не передаем :upside_down_face:. На данный момент необходимости в передаче сложных структур данных нет, нам требуются только примитивы, с которыми проблем нет. Для обратного взаимодействия (Вызова Java-методов из Rust) используются прокси-объекты - указатель на Java-объект (инстанс класса) хранится в структуре, которая реализует нужный нам трейт, вызывая Java-методы. пример

А с исключениями что делаете? Кажется размотка стека через границу FFI это UB.

Хороший вопрос. Если не ошибаюсь, начиная с 1.25 (или 1.26) - размотка стека больше не UB, просто приводит к abort. В любом случае, исключения джавы и паники раста аккуратно ловятся, приводятся в читаемый вид и печатаются в лог, что сильно помогает находить ошибки. Вспомогательный код для этого дела написан методом проб и ошибок и может выглядеть страшно


#12

Я столкнувшись с подобной проблемой написал https://github.com/Dushistov/rust_swig .

Выглядит очень любопытно, спасибо!
На данный момент мы рассматриваем различные альтернативные подходы к байндингам, думаю на этот вариант тоже взглянем.