Немного Rust в вашем C
Использование кода на Rust внутри проекта на C или C++ в основном состоит из двух частей:
- Создание API, совместимого с C, на Rust
- Встраивание вашего проекта на Rust во внешнюю систему сборки
Помимо cargo и meson, большинство систем сборки не имеют встроенной поддержки Rust. Поэтому, скорее всего, лучше всего использовать cargo для компиляции вашего крейта и любых его зависимостей.
Настройка проекта
Создайте новый проект cargo как обычно.
Есть флаги, чтобы указать cargo генерировать системную библиотеку вместо обычной цели Rust. Это также позволяет задать другое имя выходной библиотеки, если вы хотите, чтобы оно отличалось от остальной части вашего крейта.
[lib]
name = "your_crate"
crate-type = ["cdylib"] # Создает динамическую библиотеку
# crate-type = ["staticlib"] # Создает статическую библиотеку
Создание API для C
Поскольку C++ не имеет стабильного ABI для компилятора Rust, мы используем C для любой интероперабельности между разными языками. Это не исключение при использовании Rust внутри кода на C и C++.
#[no_mangle]
Компилятор Rust искажает имена символов иначе, чем ожидают компоновщики нативного кода. Поэтому любая функция, которую Rust экспортирует для использования вне Rust, должна быть помечена так, чтобы компилятор не искажал ее имя.
extern "C"
По умолчанию любая функция, написанная на Rust, использует ABI Rust (который также не стабилизирован). Вместо этого, при создании внешних API FFI, нам нужно указать компилятору использовать системный ABI.
В зависимости от вашей платформы, вы можете захотеть нацелиться на конкретную версию ABI, которые задокументированы здесь.
Собирая эти части вместе, вы получаете функцию, которая выглядит примерно так:
#[no_mangle]
pub extern "C" fn rust_function() {
}
Как и при использовании кода на C в вашем проекте на Rust, вам теперь нужно преобразовывать данные в форму, понятную остальной части приложения.
Компоновка и общий контекст проекта
Итак, одна половина проблемы решена. Как теперь это использовать?
Это очень сильно зависит от вашего проекта и/или системы сборки
cargo создаст файл my_lib.so/my_lib.dll или my_lib.a в зависимости от вашей платформы и настроек. Эту библиотеку можно просто слинковать вашей системой сборки.
Однако вызов функции Rust из C требует заголовочного файла для объявления сигнатур функций.
Каждая функция в вашем Rust-FFI API должна иметь соответствующую функцию в заголовочном файле.
#[no_mangle]
pub extern "C" fn rust_function() {}
будет преобразована в
void rust_function();
и т.д.
Существует инструмент для автоматизации этого процесса, называемый cbindgen, который анализирует ваш код на Rust и генерирует заголовочные файлы для ваших проектов на C и C++.
На этом этапе использование функций Rust из C так же просто, как включение заголовочного файла и их вызов!
#include "my-rust-project.h"
rust_function();