R パッケージで Rust を使う
がんばって説明を試みますが、素人の知識なので、いろいろ間違っていたり、雑なことを言っている可能性があります。すみません!
「C API」とは?
→ 事実上、C の ABI が共通言語として使われる
というのは難しくて理解できてません。。
→ お互いに C ABI を通じて関数を呼び出し合うことができる
(作成したパッケージのディレクトリに移動後)
#> Downloading savvy-cli binary
#> trying URL 'https://github.com/yutannihilation/savvy/releases/download/v0.8.0/savvy-cli-x86_64-unknown-linux-gnu.tar.xz'
#> Content type 'application/octet-stream' length 1412628 bytes (1.3 MB)
#> ==================================================
#> downloaded 1.3 MB
#>
#> Writing ./src/rust/Cargo.toml
#> Writing ./src/rust/.cargo/config.toml
#> ...Git を使うなら、 configure と cleanup には実行権限をつける。
Rust コードのコンパイルも実行される。
.
├── .Rbuildignore
├── DESCRIPTION
├── NAMESPACE
├── R
│ └── 000-wrappers.R
├── configure
├── configure.win
├── cleanup
├── cleanup.win
├── foo.Rproj
└── src
├── Makevars.in
├── Makevars.win.in
├── init.c
├── foo-win.def
└── rust
├── .cargo
│ └── config.toml
├── api.h
├── Cargo.toml
└── src
└── lib.rs
src/rust/src/lib.rs: Rust のコード
src/rust/api.h: コンパイルされた Rust の関数を C から呼び出すためのヘッダファイル(自動生成)
src/init.c: コンパイルされた Rust の関数を R から呼び出すための C コード(自動生成)
R/000-wrappers.R: コンパイルされた Rust の関数を呼び出す R コード(自動生成)
/// 後のコメントは R の roxygen コメントに
(#[savvy] マクロがどういう Rust コードを生成するかは、ここでは省略。引数の型のチェックとかエラー処理とかです)
関数名や引数名は、かぶらないように prefix や suffix がつく
エラー処理などの関数でラップ
R から .Call() で呼び出せるように登録
※ savvy::savvy_init() や savvy::savvy_update() は R のコードを生成するところまでで、 この roxygen コメントからドキュメントを生成するには devtools::document() が必要
src/lib.rs を編集savvy::savvy_update() で C と R のコードを生成devtools::document() で NAMESPACE とドキュメントを生成hello() を追加savvy::Result<T> なので、何も返す値がなくても () を返す(NULL になる)r_println! で R の標準出力にプリント※ document() は、新しい関数を追加したときや roxygen コメントを更新したとき以外は不要
SEXP と自分でつくった SEXP を区別するSEXP と自分でつくった SEXP を区別するwritable と同じコンセプトで、外から来た SEXP は read-only で扱うSEXP をつくって返すR では、SEXP が GC の対象にならないように PROTECT() しないといけない。その際、以下のようなルールになっている
関数の呼び出し側が渡してくる SEXP
→ 呼び出し側が PROTECT()
関数の中でつくる SEXP
→ 自分で PROTECT()
| R の型 | external | owned |
|---|---|---|
| integer | IntegerSexp |
OwnedIntegerSexp |
| double | RealSexp |
OwnedRealSexp |
| raw | RawSexp |
OwnedRawSexp |
| logical | LogicalSexp |
OwnedLogicalSexp |
| character | StringSexp |
OwnedStringSexp |
| list | ListSexp |
OwnedListSexp |
自分でつくる方の型は Owned ってついてる
関数の引数用
値にアクセスする主な方法:
.iter(): イテレータを返す.as_slice(): slice を返す(integer、numeric のみ).to_vec(): Vec を返す主に関数の return 値用
値を書き込む方法
.set_elt(i, v)IndexMut (x[i] = v)(integer、numeric のみ).into() で return 値である savvy::Result<Sexp> に変換できる
Vec<i32> や Vec<f64> などから直接 .try_into() で変換することもできるR から見ると同じに見えても、内部のデータ型が違う場合は勝手に変換したりしない。 具体的には、以下のようなケース。
そういう変換や引数のチェックは、自分でラッパー関数を書いてその中でやりましょう。
※なので、Rust の関数は直接 @export しない方がいい
savvy は R から Rust を使うためのシンプルなフレームワーク
シンプルな分、自分で書かないといけない部分は多い
仕組みを理解しつつ明示的に書くのが好きな人にはおすすめ