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 を使うためのシンプルなフレームワーク
シンプルな分、自分で書かないといけない部分は多い
仕組みを理解しつつ明示的に書くのが好きな人にはおすすめ