Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Немного Rust в вашем C

Использование кода на Rust внутри проекта на C или C++ в основном состоит из двух частей.

  • Создание дружественного к C API на 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 искажает (mangle) имена символов иначе, чем ожидают нативные компоновщики. Поэтому любой функции, которую 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 нужен заголовочный файл, который объявляет сигнатуры функций.

Каждой функции вашего FFI-API на Rust нужно сопоставить соответствующее объявление в заголовке.

#[no_mangle]
pub extern "C" fn rust_function() {}

станет

void rust_function();

и так далее.

Есть инструмент для автоматизации этого процесса — cbindgen, который анализирует ваш код на Rust и на его основе генерирует заголовки для ваших проектов на C и C++.

Теперь вызов функций Rust из C так же прост, как подключение заголовка и вызов функций!

#include "my-rust-project.h"
rust_function();