RustからScala Nativeを呼んでみる
#rust #scala Kyoto.rs 1での発表資料
$ whoami
https://scrapbox.io/files/69fefa9d5c3c134ed4281313.png
id:Windymelt / @windymelt
はてなスタッフ
好きな言語: Scala
好きなアルゴリズム: HyperLogLog
この発表
https://scrapbox.io/files/69fefd195c3c134ed4281946.png
遡ること1時間前・・・
$ mise install rust@latest
FFI
発表を聞いていたところ・・・
RustはFFIで簡単に色々呼べるらしいぞ
C ABIを満たせばいいらしい(知識無し)
Scala 3
関数型寄り言語
普通に手続き型でも書ける
GCがあるRust
Borrow CheckerがないRust
最近Capture Checkingという怪機能が追加された
マクロにステロイドが注入されたRust
Scala Native
Scalaをネイティブコンパイルしてくれる君
LLVMで動作する
普通はScala -> JVM Bytecodeにコンパイルされるところ・・・
Scala -> Native IR -> LLVM IR -> Binary
ライブラリを作れる
C ABIで関数を露出できる
ほんならRustから呼べるか・・・
Scala側のコードを書く
code:scala/project/build.properties
sbt.version = 1.12.8
ビルドツールのバージョン
Scala側のコードを書く
code:scala/project/plugins.sbt
addSbtPlugin("org.scala-native" % "sbt-scala-native" % "0.5.10")
Scala Nativeのバージョン
Scala側のコードを書く
code:scala/build.sbt
enablePlugins(ScalaNativePlugin)
scalaVersion := "3.8.3"
nativeConfig ~= {
_.withBuildTarget(scalanative.build.BuildTarget.libraryDynamic)
}
Scala側のコードを書く
code:scala/src/main/scala/Demo.scala
import scala.scalanative.unsafe.*
// 偶数だけ取り出して合計する
@exported("sum_of_evens")
def sumOfEvens(arr: PtrCInt, len: CInt): CInt =
(0 until len.toInt)
.map(i => !(arr + i))
.filter(_ % 2 == 0)
.sum
@exported("sign")
def sign(n: CInt): CInt =
n.toInt match
case x if x < 0 => -1 // 負
case 0 => 0 // ゼロ
case _ => 1 // 正
ところで・・・
本当はパーサとか書きたかった
Scalaの得意技
間に合わず
Scala Nativeでビルドする
$ sbt nativeLink
DEMO
Rustのコードを書く
code:Cargo.toml
package
name = "rust-caller"
version = "0.1.0"
edition = "2024"
code:build.rs
fn main() {
println!("cargo:rustc-link-lib=dylib=scala");
println!("cargo:rustc-link-search=native=../scala/target/scala-3.8.3/");
}
Rustのコードを書く
code:src/main.rs
unsafe extern "C" {
fn sum_of_evens(arr: *const i32, len: i32) -> i32;
fn sign(n: i32) -> i32;
}
fn main() {
let nums: Vec<i32> = vec!1, 2, 3, 4, 5, 6;
print!("[ ");
for n in nums.clone().into_iter() {
print!("{n} ");
}
println!("]");
unsafe {
// 2, 4, 6 の合計 → 12 になるはず
let total = sum_of_evens(nums.as_ptr(), nums.len() as i32);
println!("sum_of_evens: {}", total);
// パターンマッチの結果確認
for n in -3, 0, 4, 7 {
println!("sign({:>3}) => {}", n, sign(n));
}
}
}
ビルドする
$ cargo build
$ LD_LIBRARY_PATH=../scala/target/scala-3.8.3/ cargo run
DEMO
感想
Rustはビルドが速い
うらやましい
Rustはi32とかi64とかがあってたのもしい
うらやましい
よかったね
RustとScalaのいいとこどりができた
Scalaは勝手にランタイムがGCするので放置していてよい