S-JIS[2024-10-13/2024-10-15] 変更履歴

Rust externメモ

Rustのexternのメモ。


概要

Rustから(C ABI形式で書かれた)他言語の関数を呼び出すことが出来る。


Rustから他言語の関数を呼び出す例

呼び出す他言語の関数は、Rustのソースコードの中にextern "C"ブロックを作って宣言する。
また、受け渡す構造体には#[repr(C)]を付けてC言語形式にする。

use std::ffi::{c_char, CStr, CString};
#[repr(C)]
struct Int64Result {
    value: i64,                  	// int64_t
    error_message: *const c_char,	// const char*
}
extern "C" {
    // Int64Result createContainer(const char* name);
    fn createContainer(name: *const c_char) -> Int64Result;

    // void closeContainer(int64_t handle);
    fn closeContainer(handle: i64);
}

呼ぶ関数の内部がC++の場合、エラーがあったら例外をスローするが、C言語形式の場合は例外機構は無い。
そこで、関数から返す構造体を用意し、その中でエラーメッセージを返すことにする。


最後に必ずクローズ処理を呼ばないといけないなら、専用の構造体を作ってDropトレイトを実装しておくのが良さそう。

pub(crate) struct ExternContainer {
    handle: i64,
}
impl ExternContainer {
    pub(crate) fn new(name: &str) -> Result<ExternContainer, String> {
        let name = CString::new(name).unwrap();

        let result = unsafe { createContainer(name.as_ptr()) };
        if !result.error_message.is_null() {
            let error_message = unsafe { CStr::from_ptr(result.error_message) };
            return Err(format!("{}", error_message.to_str().unwrap()));
        }

        let container = ExternContainer {
            handle: result.value,
        };
        Ok(container)
    }
}
impl Drop for ExternContainer {
    fn drop(&mut self) {
        unsafe {
            closeContainer(self.handle);
        }
    }
}

ビルド(コンパイル)する際には、呼び出す関数のライブラリーを指定する必要がある。

プロジェクト/build.rs:

fn main() {
    println!("cargo:rustc-link-search=native=/path/to/lib_dir");
    println!("cargo:rustc-link-lib=dylib=example");
}

上記の指定で、「/path/to/lib_dir/libexample.so」が読み込まれる。


さらに、実行時に、環境変数LD_LIBRARY_PATHにsoファイルのディレクトリーを入れておく必要がある。

$ export LD_LIBRARY_PATH=/path/to/lib_dir

このように、extern "C"の場合はコンパイル時や実行時に外部ライブラリーが必要となる。[2024-10-15]
(特に実行時に境変数LD_LIBRARY_PATHが必須となるが、LD_LIBRARY_PATHは他のライブラリーとも共通なので、あまり使用したくない)

これらを設定せずに動的に外部ライブラリーを読み込むには、extern "C"を使用せず、libloadingクレートを利用する。


Rustへ戻る / 技術メモへ戻る
メールの送信先:ひしだま