RustのFFI(Foreign Function Interface)と他言語連携

Rustは、その高いパフォーマンスとメモリ安全性を活かして、他のプログラミング言語と連携するための**FFI(Foreign Function Interface)**を提供しています。

FFIを使うことで、RustのコードをCやC++などの他言語から呼び出したり、逆にRustから他言語のコードを呼び出すことが可能です。

この記事では、Rustと他言語の連携方法を学び、FFIの基本的な使い方を紹介します。


FFIとは?

**FFI(Foreign Function Interface)**とは、異なるプログラミング言語間で関数やデータをやり取りするための仕組みです。

Rustでは、特にC言語との連携が強力で、Cの関数を呼び出したり、Rustの関数をC言語から利用することができます。

RustとCの連携のメリット

  • 高パフォーマンス: RustはCと同等のパフォーマンスを持つため、既存のCコードにRustを組み込んでも効率が低下しません。
  • 安全性: Rustのメモリ安全性を保ちながら、Cの機能を活用できるため、セキュアなシステムの構築に役立ちます。

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

RustからC言語の関数を呼び出す場合、C言語で定義された関数のシグネチャをRustで宣言し、それを呼び出せるようにします。

C言語のコード

まず、C言語のコードを用意します。

この例では、単純な add 関数をCで定義し、Rustから呼び出します。

#include <stdio.h>

int add(int a, int b) {
    return a + b;
}

RustからCの関数を呼び出す

次に、RustのコードでC言語の add 関数を宣言し、FFIを通じて呼び出します。

Rustのコード

extern "C" {
    fn add(a: i32, b: i32) -> i32;
}

fn main() {
    let result = unsafe { add(5, 7) };
    println!("5 + 7 = {}", result);
}

ポイント:

  • extern "C" ブロック内でC言語の関数を宣言します。
  • unsafe ブロック内で関数を呼び出します。これは、RustがFFI呼び出しをメモリ安全と見なさないためです。

コンパイルとリンク

Cのコードをコンパイルし、Rustのコードとリンクします。

$ gcc -c -o add.o add.c
$ ar rcs libadd.a add.o
$ rustc main.rs -l add -L .
  • gcc コマンドでCコードをコンパイルし、Rustのプロジェクトにリンクします。
  • rustc コマンドでRustコードをCのライブラリとリンクさせてビルドします。

RustをC言語から呼び出す

逆に、Rustの関数をC言語から呼び出すことも可能です。

Rustで関数を #[no_mangle] 属性を使って定義し、Cから利用します。

Rustでの関数定義

Rustで定義した関数をCから呼び出すためには、関数の名前を「マングリング」しないように #[no_mangle] 属性を使います。

#[no_mangle]
pub extern "C" fn multiply(a: i32, b: i32) -> i32 {
    a * b
}

ポイント:

  • #[no_mangle] 属性を使うことで、Rustのコンパイラが関数名を変更しないようにします。
  • extern "C" を使ってCと互換性のある関数呼び出しを可能にします。

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

次に、CのコードからRustの関数を呼び出します。

Cのコード

#include <stdio.h>

extern int multiply(int a, int b);

int main() {
    int result = multiply(6, 7);
    printf("6 * 7 = %d\n", result);
    return 0;
}

コンパイルとリンク

Rustのコードをライブラリとしてコンパイルし、C言語のコードとリンクします。

$ rustc --crate-type=staticlib lib.rs
$ gcc main.c -o main -L . -l lib

これで、C言語からRustの関数を呼び出して利用できます。


Rustと他言語の連携例

Rustは、C以外の言語ともFFIを通じて連携できます。ここでは、Pythonとの連携例を見てみましょう。

RustのコードをPythonから呼び出すために、pyo3 というクレートを使用します。

pyo3 クレートのインストール

まず、pyo3 クレートを使ってRustとPythonを連携させます。

$ cargo new --lib rust_python_example
$ cd rust_python_example
$ echo 'pyo3 = "0.15"' >> Cargo.toml

Rustのコード

以下は、RustでPythonから呼び出す関数を定義した例です。

use pyo3::prelude::*;

#[pyfunction]
fn sum_as_string(a: i64, b: i64) -> PyResult<String> {
    Ok((a + b).to_string())
}

#[pymodule]
fn rust_python_example(py: Python, m: &PyModule) -> PyResult<()> {
    m.add_function(wrap_pyfunction!(sum_as_string, m)?)?;
    Ok(())
}

PythonからRustの関数を呼び出す

Rustのコードをビルドし、Pythonで使えるライブラリを作成します。

$ maturin develop

その後、PythonからRustの関数を呼び出せます。

import rust_python_example

result = rust_python_example.sum_as_string(10, 20)
print(f"10 + 20 = {result}")

練習問題

練習問題1: RustからC関数の呼び出し

次のステップに従って、RustからC言語の関数を呼び出し、その結果をRustで処理するプログラムを作成してください。

  1. C言語で multiply 関数を定義し、2つの整数を掛け算する関数を作成します。
  2. Rustからその関数を呼び出し、結果を表示するプログラムを実装してください。

まとめ

今回の記事では、RustのFFI(Foreign Function Interface)を使って他言語と連携する方法について学びました。

Rustは、CやPythonなどの他のプログラミング言語と簡単に連携できるため、既存のシステムにRustを組み込んでパフォーマンスを向上させることが可能です。

Rustで提供されていないライブラリや機能が必要な場合でも、Cのライブラリを使うことで、Rustのライブラリ不足を補うことができます。

これにより、Rustのエコシステムにまだ存在しない特定の機能やライブラリも、C言語や他の言語の豊富な資産を活用することで解決できるようになります。

次回予告のデザイン

次回予告

次回は基礎編の最終回、「Rustで構築する最終プロジェクト:学んだ知識をまとめよう」をテーマに、これまで学んだ内容をまとめて最終プロジェクトを作成します。