RustからFortranライブラリを利用するための基礎手順を徹底解説

RustからFortranライブラリを利用することで、高速で効率的な数値計算や科学技術計算が可能になります。Rustは安全性とパフォーマンスに優れたシステムプログラミング言語であり、一方でFortranは科学技術計算分野で長年の実績を持つ言語です。これらを組み合わせることで、Rustの安全性とFortranの高い計算性能を両立させることができます。本記事では、RustからFortranライブラリを呼び出すための基礎手順やその仕組み、データ型の対応、デバッグ方法、応用例について詳細に解説します。

RustとFortranの連携概要


RustとFortranは異なる設計思想を持つ言語ですが、FFI(Foreign Function Interface)を利用することでシームレスに連携できます。Fortranは数値計算や科学技術計算に特化した言語で、数十年にわたり実績があります。一方、Rustはメモリ安全性と高パフォーマンスを両立する言語です。

これらの言語を連携させることで、以下のようなメリットがあります:

  1. Fortranの高性能ライブラリを再利用:Fortranで書かれた高度な数値計算ライブラリをRustのプロジェクトで活用できます。
  2. Rustの安全性とモダンな機能:Rustの型安全性やエラーハンドリングを活かしながらFortran関数を呼び出せます。
  3. パフォーマンス向上:RustとFortranはネイティブコードにコンパイルされるため、オーバーヘッドが少なく高パフォーマンスを維持できます。

次のセクションでは、FFIの基本概念について説明し、RustとFortranがどのように連携するのかを理解します。

FFI(Foreign Function Interface)とは


FFI(Foreign Function Interface)は、異なるプログラミング言語間で関数を呼び出し合うための仕組みです。RustからFortranの関数を呼び出す際にも、このFFIを活用します。FFIを使うことで、RustのコードからC、C++、Fortranなどで書かれたライブラリをシームレスに利用できます。

FFIの基本構造


RustでFFIを利用するには、externキーワードとunsafeブロックを用います。基本的なFFIの構造は以下の通りです。

extern "C" {
    fn some_fortran_function(arg: i32) -> f64;
}

fn main() {
    unsafe {
        let result = some_fortran_function(42);
        println!("Result: {}", result);
    }
}

FFIにおける「C」呼び出し規約


RustとFortranのFFIでは、通常C呼び出し規約extern "C")を使用します。C言語は多くの言語で共通の呼び出し規約を持つため、RustからFortran関数を呼び出す際もこの規約に準拠することで互換性が保たれます。

FFIを使用する際の注意点


FFIを利用する際には以下の点に注意が必要です。

  1. 安全性の保証がない:FFIを通じた関数呼び出しはunsafeブロック内で行う必要があります。Rustの安全性保証が適用されないため、メモリ管理や型の整合性に注意が必要です。
  2. データ型の互換性:RustとFortranでのデータ型の違いに注意し、正しい型をマッピングする必要があります。
  3. リンカ設定:FortranライブラリをRustとリンクする際には、正しいビルド設定やリンカの指定が求められます。

次のセクションでは、Fortranライブラリを実際に準備し、Rustから呼び出すためのビルド方法について解説します。

Fortranライブラリの準備とビルド方法


Rustから呼び出すためには、まずFortranでライブラリを作成し、ビルドする必要があります。以下では、Fortranの関数を定義し、共有ライブラリとしてビルドする手順を解説します。

1. Fortran関数の作成


Fortranで簡単な関数を作成します。ここでは、2つの数値の合計を計算する関数の例です。

sum.f90

module math_operations
    implicit none
contains
    function add_numbers(a, b) result(sum)
        real :: a, b, sum
        sum = a + b
    end function add_numbers
end module math_operations

この関数は2つのreal型引数を受け取り、その合計を返します。

2. Fortranライブラリのビルド


次に、Fortranコードを共有ライブラリ(libsum.so)としてビルドします。Unix系システムではgfortranを使用します。

gfortran -shared -fPIC -o libsum.so sum.f90
  • -shared:共有ライブラリとしてビルドするオプションです。
  • -fPIC:位置独立コードを生成するためのオプションです。
  • libsum.so:生成される共有ライブラリの名前です。

3. 生成されたライブラリの確認


ビルドが成功すると、カレントディレクトリにlibsum.soが生成されます。確認するには次のコマンドを使用します。

ls -l libsum.so

これでRustから呼び出す準備が整いました。次のセクションでは、Rustプロジェクトのセットアップ方法について説明します。

Cargoプロジェクトのセットアップ


RustでFortranライブラリを呼び出すためには、Cargoを用いてRustプロジェクトをセットアップし、外部ライブラリをリンクする設定を行います。以下の手順で進めていきます。

1. Cargoプロジェクトの作成


まず、新しいCargoプロジェクトを作成します。ターミナルで次のコマンドを実行してください。

cargo new rust_fortran_example
cd rust_fortran_example

これで基本的なRustプロジェクトのディレクトリが作成されます。

2. Fortranライブラリの配置


ビルド済みのFortranライブラリ(libsum.so)をプロジェクトのsrcディレクトリまたはルートディレクトリにコピーします。

cp /path/to/libsum.so ./libsum.so

3. Cargo設定ファイルの編集


Cargo.tomlにライブラリのリンク設定を追加します。以下の設定をCargo.tomlに記述します。

Cargo.toml

[package]
name = "rust_fortran_example"
version = "0.1.0"
edition = "2021"

[dependencies]

[build] rustflags = [“-L.”, “-lsum”]

  • -L.:現在のディレクトリをライブラリ検索パスとして指定します。
  • -lsumlibsum.soをリンクすることを指定します。

4. Rustで外部ライブラリを宣言


RustのコードでFortran関数を呼び出すために、FFIの宣言を追加します。

src/main.rs

extern "C" {
    fn add_numbers(a: f32, b: f32) -> f32;
}

fn main() {
    let a: f32 = 5.0;
    let b: f32 = 3.0;

    unsafe {
        let result = add_numbers(a, b);
        println!("The sum of {} and {} is {}", a, b, result);
    }
}

5. プロジェクトのビルドと実行


最後に、プロジェクトをビルドして実行します。

cargo run

正しく設定されていれば、次のような出力が得られます。

The sum of 5 and 3 is 8

これでRustからFortranライブラリを呼び出すためのCargoプロジェクトのセットアップは完了です。次のセクションでは、RustからFortran関数を実際に呼び出す手順を詳しく解説します。

RustでFortran関数を呼び出す手順


RustでFortran関数を呼び出すには、FFI(Foreign Function Interface)を用いたexternブロックと、unsafeブロックを使用します。ここでは、具体的な手順とコード例を解説します。

1. Fortran関数の定義


まず、呼び出すFortran関数を確認します。以下は、2つの浮動小数点数を受け取り、その合計を返すFortran関数です。

sum.f90

module math_operations
    implicit none
contains
    function add_numbers(a, b) result(sum)
        real :: a, b, sum
        sum = a + b
    end function add_numbers
end module math_operations

この関数はreal型引数2つを受け取り、合計を返します。

2. Fortranライブラリのビルド


Fortranコードを共有ライブラリとしてビルドします。

gfortran -shared -fPIC -o libsum.so sum.f90

3. RustでFFI宣言


Rust側でFortran関数を呼び出すため、FFI宣言を追加します。extern "C"ブロックでFortran関数を宣言します。

src/main.rs

extern "C" {
    fn add_numbers(a: f32, b: f32) -> f32;
}

fn main() {
    let a: f32 = 4.5;
    let b: f32 = 2.5;

    unsafe {
        let result = add_numbers(a, b);
        println!("The sum of {} and {} is {}", a, b, result);
    }
}

4. コード解説

  • extern "C":Fortran関数をC呼び出し規約でリンクすることを指定します。
  • fn add_numbers(a: f32, b: f32) -> f32:Fortran関数をRustで呼び出せるように宣言しています。
  • unsafeブロック:FFIを使った関数呼び出しは安全性が保証されないため、unsafeブロック内で実行します。

5. ビルドと実行


Cargoを使ってプロジェクトをビルドし、実行します。

cargo run

出力結果

The sum of 4.5 and 2.5 is 7.0

注意点

  1. ライブラリのパス:Rustがlibsum.soを正しく見つけられるように、ライブラリのパスを環境変数LD_LIBRARY_PATHに追加する必要があります。
   export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
  1. データ型の一致:RustとFortranのデータ型が一致していることを確認してください。Rustのf32はFortranのrealと互換性があります。

これでRustからFortran関数を呼び出す手順が完了です。次のセクションでは、データ型の対応と注意点について解説します。

Fortran関数の型対応と注意点


RustとFortranは異なるデータ型体系を持つため、FFIを使って関数を呼び出す際には、データ型の対応に注意が必要です。ここでは、主なデータ型のマッピングと、注意すべきポイントについて解説します。

RustとFortranのデータ型の対応表


RustとFortran間で利用する一般的なデータ型のマッピングは以下の通りです。

Fortranデータ型Rustデータ型説明
integeri3232ビット符号付き整数
realf3232ビット浮動小数点数
double precisionf6464ビット浮動小数点数
logicalbool真偽値(Rustではbool型)
character(len=*)*const c_charC互換の文字列ポインタ

Rust側でのFFI宣言例


Fortran関数が異なるデータ型を返す場合、Rustでの宣言は以下のように行います。

Fortran関数定義(sum.f90

module math_operations
    implicit none
contains
    function multiply_numbers(a, b) result(product)
        real :: a, b, product
        product = a * b
    end function multiply_numbers
end module math_operations

Rust側FFI宣言(src/main.rs

extern "C" {
    fn multiply_numbers(a: f32, b: f32) -> f32;
}

fn main() {
    let a: f32 = 4.0;
    let b: f32 = 3.0;

    unsafe {
        let result = multiply_numbers(a, b);
        println!("The product of {} and {} is {}", a, b, result);
    }
}

注意点

1. データ型のサイズと整合性


RustとFortranでデータ型のサイズやエンディアンが一致しない場合、予期しない挙動が発生する可能性があります。例えば、Rustのi32はFortranのintegerとほぼ互換性がありますが、コンパイラやプラットフォームによってサイズが異なることがあります。

2. 文字列の取り扱い


Fortranのcharacter型とRustの文字列は直接互換性がありません。文字列を渡す場合、C互換の文字列ポインタ(*const c_char)を使用し、適切に変換する必要があります。

3. メモリ管理


Fortran関数内で動的に確保されたメモリは、Rust側で安全に解放できるとは限りません。メモリ管理に関しては明示的にルールを決める必要があります。

4. `unsafe`ブロック


FFI呼び出しはRustの安全性保証が適用されないため、すべてunsafeブロック内で行う必要があります。バグや未定義動作の原因になりやすいため、呼び出し前にしっかりとテストを行いましょう。

型の対応が適切でない場合のトラブルシューティング

  • 型エラーが発生したら、RustとFortranで使用している型が一致しているか確認してください。
  • リンクエラーが出る場合、Fortranライブラリが正しくビルドされ、Rustプロジェクトがそれを参照しているか確認してください。
  • メモリ破壊が疑われる場合、ポインタやバッファのサイズに注意し、オーバーフローが起きていないか確認しましょう。

次のセクションでは、デバッグとトラブルシューティングの具体的な方法について解説します。

デバッグとトラブルシューティング


RustとFortranの連携では、さまざまなエラーが発生する可能性があります。ここでは、一般的なエラーの種類、デバッグ手法、および問題を解決するためのトラブルシューティング方法について解説します。

1. コンパイルエラーとリンクエラー

コンパイルエラーの例


RustやFortranのコードに構文エラーや型の不一致があると、コンパイル時にエラーが発生します。

例: Rustでの型不一致エラー

extern "C" {
    fn add_numbers(a: i32, b: i32) -> f32; // Rust側でi32として宣言
}

fn main() {
    unsafe {
        let result = add_numbers(5, 3); // Fortran関数がf32を期待している場合にエラー
        println!("Result: {}", result);
    }
}

解決方法

  • RustとFortranでデータ型が一致しているか確認してください。Rust側の宣言とFortran側の関数定義が一致する必要があります。
  • Fortranコード内で引数や戻り値の型を適切に定義しましょう。

リンクエラーの例


RustがFortranライブラリを正しく見つけられない場合、リンクエラーが発生します。

エラーメッセージ例

error: linking with `cc` failed: exit code: 1
note: ld: library not found for -lsum

解決方法

  • Fortranライブラリのパスが正しく設定されているか確認してください。
  • 環境変数LD_LIBRARY_PATHにライブラリのパスを追加します。
  export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
  • Cargo.tomlのリンカ設定が正しいか確認しましょう。

2. 実行時エラー

セグメンテーション違反


不正なメモリアクセスにより、セグメンテーション違反が発生することがあります。

原因

  • ポインタの不正な参照
  • データ型の不一致によるメモリ破壊

解決方法

  • RustとFortranでデータ型が一致していることを再確認してください。
  • ポインタを使う場合、正しいメモリ領域にアクセスしているか確認します。
  • デバッグツール(例: gdblldb)を使用してクラッシュの原因を特定します。
  gdb ./target/debug/rust_fortran_example

3. デバッグ方法

Rustの`println!`デバッグ


Rustコードにprintln!マクロを追加して、変数の値や関数の呼び出し状況を確認します。

println!("Calling Fortran function with a = {}, b = {}", a, b);

Fortranのデバッグ出力


Fortran側でもprint文を追加して関数が正しく呼び出されているか確認します。

Fortranコード例

print *, "Fortran function called with a =", a, " b =", b

GDBを使用したデバッグ


GDBを使ってRustとFortranのデバッグ情報を表示します。

gdb --args cargo run

GDB内でブレークポイントを設定し、ステップ実行することで詳細な情報を得られます。

4. よくあるエラーと解決策

エラーの種類原因解決策
ライブラリが見つからないライブラリパスが設定されていないLD_LIBRARY_PATHを正しく設定する
型不一致RustとFortranのデータ型が異なる型を確認し、一致するように修正する
セグメンテーション違反ポインタの不正アクセス、メモリ破壊ポインタと型の整合性を確認する
関数が呼び出せないexternブロックの関数名が間違っている関数名や呼び出し規約を確認する

まとめ


デバッグとトラブルシューティングでは、エラーメッセージを丁寧に確認し、RustとFortranのコードや設定を見直すことが重要です。特にデータ型やライブラリのリンク設定は、問題の原因になりやすいため、注意して設定しましょう。次のセクションでは、Fortranライブラリを活用した具体的な応用例について解説します。

応用例:数値計算ライブラリの活用


RustからFortranライブラリを呼び出すことで、科学技術計算や数値解析を効率的に行うことができます。ここでは、Fortranで実装された数値計算ライブラリをRustから呼び出す具体的な応用例を紹介します。

1. Fortranで数値積分関数を実装


以下のFortranコードは、シンプソンの公式を用いた数値積分の関数です。

integration.f90

module numerical_methods
    implicit none
contains
    function integrate(f, a, b, n) result(res)
        real :: a, b, res
        integer :: n
        interface
            function f(x) result(y)
                real :: x, y
            end function f
        end interface
        real :: h, x
        integer :: i

        h = (b - a) / real(n)
        res = f(a) + f(b)

        do i = 1, n-1
            x = a + i * h
            if (mod(i, 2) == 0) then
                res = res + 2.0 * f(x)
            else
                res = res + 4.0 * f(x)
            end if
        end do

        res = res * h / 3.0
    end function integrate
end module numerical_methods

この関数は、関数f[a, b]の区間でn分割して積分を行います。

2. Fortranライブラリのビルド


Fortranコードを共有ライブラリとしてビルドします。

gfortran -shared -fPIC -o libintegration.so integration.f90

3. RustでFortran関数を呼び出す


Rust側でFortranライブラリの数値積分関数を呼び出すため、FFI宣言とコールバック関数を設定します。

src/main.rs

use std::ffi::c_float;

extern "C" {
    fn integrate(
        f: extern "C" fn(x: c_float) -> c_float,
        a: c_float,
        b: c_float,
        n: i32,
    ) -> c_float;
}

// コールバック関数:被積分関数 f(x) = x^2
extern "C" fn square(x: c_float) -> c_float {
    x * x
}

fn main() {
    let a: c_float = 0.0;
    let b: c_float = 1.0;
    let n: i32 = 100;

    unsafe {
        let result = integrate(square, a, b, n);
        println!("The integral of x^2 from {} to {} is approximately {}", a, b, result);
    }
}

4. プロジェクトのビルドと実行


Cargoを使用してプロジェクトをビルドし、実行します。

cargo run

出力結果

The integral of x^2 from 0 to 1 is approximately 0.3333333

解説

  • Fortran関数 integrate:シンプソンの公式で数値積分を行います。
  • Rustのコールバック関数 squarex^2という被積分関数です。
  • FFIを用いた関数呼び出し:RustからFortran関数にコールバック関数を渡し、数値積分を実行しています。

5. 応用の可能性


RustとFortranの連携により、以下のような高度なアプリケーションが実現できます:

  • シミュレーションツール:Fortranで実装されたシミュレーションアルゴリズムをRustのシステムに統合。
  • 大規模数値計算:行列演算や微分方程式の数値解法をRustの安全性とともに活用。
  • データ解析:Rustの並行処理やエラーハンドリングを用いて、Fortranライブラリで高速なデータ解析。

次のセクションでは、これまでの内容を振り返るまとめを行います。

まとめ


本記事では、RustからFortranライブラリを呼び出すための基礎手順について解説しました。Rustの安全性とFortranの高い数値計算性能を組み合わせることで、効率的で堅牢なアプリケーション開発が可能になります。

内容を振り返ると、以下のポイントを学びました:

  1. RustとFortranの連携概要:FFI(Foreign Function Interface)を使用して言語間の関数呼び出しを実現する仕組み。
  2. Fortranライブラリの準備:Fortranコードを共有ライブラリとしてビルドする方法。
  3. Cargoプロジェクトのセットアップ:RustプロジェクトでFortranライブラリをリンクする設定。
  4. RustからFortran関数の呼び出し:FFIを用いた具体的なコード例。
  5. データ型の対応と注意点:RustとFortran間でのデータ型のマッピングと互換性。
  6. デバッグとトラブルシューティング:よくあるエラーへの対処法とデバッグ手法。
  7. 応用例:数値積分の具体例を通じたRustとFortranの活用法。

RustとFortranの組み合わせにより、高速計算が求められるシステム開発や科学技術計算が効率的に行えます。これらの知識を活かし、より高度なアプリケーション開発に挑戦してみてください。

コメント

コメントする