Cと少しのRust
CまたはC++のプロジェクト内でRustのコードを使うためには、主に次の2つの対応をします。
- Cが扱いやすいAPIをRustに作ります
- 外部ビルドシステムにRustプロジェクトを組み込みます
cargo
とmeson
は別として、ほとんどのビルドシステムはRustをサポートしていません。
そのため、自分のクレートとその依存関係をコンパイルするには、cargo
を使うのが一番です。
プロジェクトの準備
いつもどおり、新しいcargo
プロジェクトを作成します。
通常のRustのターゲットではなく、システムライブラリを出力するように、cargo
に指示するフラグがあります。
クレートの残り部分と異なる名前を付けたい場合、ライブラリに対して、別の名前を設定することもできます。
[lib]
name = "your_crate"
crate-type = ["cdylib"] # 動的ライブラリを作ります
# crate-type = ["staticlib"] # 静的ライブラリを作ります
C
APIの作成
C++は、Rustコンパイラがターゲットにできる安定したABIを持っていないため、別言語との相互運用には、CのABIを使用します。 CとC++のコード内でRustを使うとき、このことに例外はありません。
#[no_mangle]
Rustコンパイラは、シンボル名をネイティブコードリンカが期待するものとは異なるものにマングルします。 そのため、Rustの外にエクスポートするRustの関数は、マングルしないようにコンパイラに指示する必要があります。
extern "C"
デフォルトでは、Rustに書いたいずれの関数もRustのABI(これも安定化されていません)を使います。 代わりに、外に公開するFFI APIはシステムABIを使うように、コンパイラに指示する必要があります。
プラットフォームによっては、特定のABIバージョンをターゲットにしたい場合があります。 ABIについては、ここにドキュメントがあります。
これらの部品をまとめると、おおよそ次のような関数になります。
#[no_mangle]
pub extern "C" fn rust_function() {
}
RustプロジェクトでC
コードを使った時と同様に、異なる言語間で理解できるデータ型に変換する必要があります。
リンクとより大きなプロジェクトの状況
ここまでで、問題の半分は解決しました。 これをどうやって使うのでしょうか?
それは、プロジェクトやビルドシステムに強く依存します。
cargo
は、プラットフォームと設定に依存して、my_lib.so
、my_lib.dll
またはmy_lib.a
ファイルを作成します。
このライブラリは、そのプラットフォームのビルドシステムによって簡単にリンクすることができます。
しかし、CからRustを呼ぶためには、関数シグネチャを宣言するためのヘッダファイルが必要です。
Rust-ffi APIの関数全てが、対応する関数ヘッダを持つ必要があります。
#[no_mangle]
pub extern "C" fn rust_function() {}
上記は、次のようになるでしょう。
void rust_function();
などなど。
このプロセスを自動化するcbindgenというツールがあります。 このツールは、Rustのコードを解析して、CとC++プロジェクトのためのヘッダを生成します。
この時点で、CからRustの関数を使うには、単にヘッダをインクルードして、それを呼び出すだけです!
#include "my-rust-project.h"
rust_function();