Slide 1

Slide 1 text

Rパッケージで Rustを使うには: extendr入門 Hiroaki Yutani (@yutannihilation) Tokyo.R#92

Slide 2

Slide 2 text

ドーモ! : @yutannihilation 好きな言語: R、Rust、忍殺語 最近の趣味: ガスコンロの電子楽器 をつくってます

Slide 3

Slide 3 text

Rユーザのための RStudio[実践]入 門 第2版! https://gihyo.jp/book/2021/978- 4-297-12170-9 紙は6月3日、電子は5 月31日発売です。

Slide 4

Slide 4 text

extendr

Slide 5

Slide 5 text

extendrとは? RustとRを連携させるためのフレームワーク RからRustを使うだけではなく、RustからRを使 うこともできる(つまり、Rustの中でggplot2を 呼び出してプロットしたり、とかできるらしい) なぜか私も中の人です…

Slide 6

Slide 6 text

なぜRust? → そこにRustがあるから!! (誰か教えてください…)

Slide 7

Slide 7 text

※今日話さないこと Rustの何が素晴らしいのか Rust入門 Rust側からRを操作する方法 R MarkdownのRust engineとか、パッケージ外 でのextendrの使いみち

Slide 8

Slide 8 text

extendrの愉快な仲間たち libR-sys(Rust): RのC APIにbindgenで生成したバインディング extendr(Rust): libR-sysを使いやすくラップしたフレームワーク rextendr(Rパッケージ): Rからextendrを使うためのユーティリティ (usethisパッケージのような立ち位置)

Slide 9

Slide 9 text

準備

Slide 10

Slide 10 text

Rustのインストール macOS / Linux: ふつうにRustをインストール(ググる) Windows MSVCのtoolchainに加えて、64bit/32bit GNU用 のtargetを追加する必要がある rustup default stable-msvc rustup target add x86_64-pc-windows-gnu rustup target add i686-pc-windows-gnu

Slide 11

Slide 11 text

rextendrパッケージのインストール GitHubからインストール devtools::install_github("extendr/rextendr")

Slide 12

Slide 12 text

パッケージのセットアップ

Slide 13

Slide 13 text

RStudioからパッケージ作成

Slide 14

Slide 14 text

Roxygenを使うように設定変更 NAMESPACEを上書き Build optionsを設定 Build > Configure Build Tools… > Generate documentation with Roxygen に を入れる 不要なファイルを削除 R/hello.R man/hello.Rd usethis::use_namespace() ` ` ` `

Slide 15

Slide 15 text

extendrのデフォルト設定を生成 rextendr::use_extendr() ✓ Creating src/rust/src. ✓ Writing 'src/entrypoint.c' ✓ Writing 'src/Makevars' ✓ Writing 'src/Makevars.win' ✓ Writing 'src/.gitignore' ✓ Writing src/rust/Cargo.toml. ✓ Writing 'src/rust/src/lib.rs' ✓ Writing 'R/extendr-wrappers.R' ✓ Finished configuring extendr for package myextendr. • Please update the system requirement in DESCRIPTION file. • Please run `rextendr::document()` for changes to take effect

Slide 16

Slide 16 text

生成されたファイル . ├── R │ └── extendr-wrappers.R ... └── src ├── Makevars ├── Makevars.win ├── entrypoint.c └── rust ├── Cargo.toml └── src └── lib.rs

Slide 17

Slide 17 text

いじるファイル src/rust: extendrを使ったRustのcrate。開発のメインはこ こ。

Slide 18

Slide 18 text

基本いじらないファイル Makevars, Makevars.win: パッケージインストール時に cargo build が走るように する設定。 entrypoint.c: コンパイラにシンボルを勝手に消されないためのおま じない。 R/extendr-wrappers.R: Rustの関数から自動生成されたRの関数。 ` `

Slide 19

Slide 19 text

src/rust/Cargo.toml [package] name = 'myextendr' version = '0.1.0' edition = '2018' [lib] crate-type = [ 'staticlib' ] [dependencies] extendr-api = '*'

Slide 20

Slide 20 text

src/rust/src/lib.rs(一部省略) use extendr_api::prelude::*; /// Return string `"Hello world!"` to R. /// @export #[extendr] fn hello_world() -> &'static str { "Hello world!" } extendr_module! { mod myextendr; fn hello_world; }

Slide 21

Slide 21 text

src/rust/src/lib.rs よく使う関数をまとめて読み込み /// (3つ)のコメントはそのままRoxygenのコ メントになる これをつけるとRの関数が自動生成! use extendr_api::prelude::*; ` ` /// Return string `"Hello world!"` to R. /// @export #[extendr]

Slide 22

Slide 22 text

src/rust/src/lib.rs 関数をエクスポートしてRが認識できるように登録 (routine registration)してくれるマクロ。 新 しく関数を追加したらここに入れる必要がある。 extendr_module! { mod myextendr; fn hello_world; }

Slide 23

Slide 23 text

開発の流れ

Slide 24

Slide 24 text

開発の流れ 1. Rustのコードを編集 2. rextendr::document() でRのコードを自動生成(Rust のコードのコンパイルもこれがやってくれる) 3. (必要あれば)生成されたコードをRの側でいい感 じにラップする 4. devtools::load_all() (やテスト)で動作確認 ` ` ` `

Slide 25

Slide 25 text

rextendr::document() ` ` > rextendr::document() ✓ Saving changes in the open files. ℹ Generating extendr wrapper functions for package: myextendr. ! No library found at src/myextendr.so, recompilation is requi Re-compiling myextendr ─ installing *source* package ‘myextendr’ ... (382ms) ** using staged installation ** libs rm -Rf myextendr.so ./rust/target/release/libmyextendr.a en gcc -std=gnu99 -I"/usr/share/R/include" -DNDEBUG -fpic cargo build --lib --release --manifest-path=./rust/Cargo.to Updating crates.io index

Slide 26

Slide 26 text

生成されるファイル . ... ├── NAMESPACE <- @exportが反映される ├── R │ └── extendr-wrappers.R <- Rustの関数から自動生成 └── src ├── myextendr.so <- libmyextendr.aを使ってビルドされた └── rust 共有オブジェクト └── target <- Rustの生成物が入るディレクトリ └── release ├── libmyextendr.a <- Rustのコードの静的ライブラリ ... (OSによって拡張子は違う)

Slide 27

Slide 27 text

自動生成されたRの関数 Rust R /// Return string `"Hello world!"` to R. /// @export #[extendr] fn hello_world() -> &'static str { "Hello world!" } #' Return string `"Hello world!"` to R. #' @export hello_world <- function() .Call(wrap__hello_world)

Slide 28

Slide 28 text

実行結果 #> [1] "Hello world!" devtools::load_all(".") hello_world()

Slide 29

Slide 29 text

例1) i32 (integer)を引数に取る 関数

Slide 30

Slide 30 text

自動生成されたRの関数 Rust R /// @export #[extendr] fn add(x: i32, y: i32) -> i32 { x + y } #' @export add <- function(x, y) .Call(wrap__add, x, y)

Slide 31

Slide 31 text

実行結果 #> [1] 3 #> Error in add(1:2, 2:3) : #> Input must be of length 1. Vector of length >1 given. devtools::load_all(".") # 引数の型は i32 だけど実数も渡せる add(1, 2) # 長さ1以上だとエラーになる add(1:2, 2:3)

Slide 32

Slide 32 text

例2) Vecを引数に取る関数

Slide 33

Slide 33 text

自動生成されたRの関数 Rust R #[extendr] fn mult(x: Vec, y: i32) -> Vec { x .iter() .map(|n| n * y) .collect::>() } #' @export mult <- function(x, y) .Call(wrap__mult, x, y)

Slide 34

Slide 34 text

実行結果 #> [1] 10 20 30 40 50 devtools::load_all(".") mult(1:5, 10)

Slide 35

Slide 35 text

例3) struct

Slide 36

Slide 36 text

struct…? 環境としてエクスポートされるので状態を持つこ とができる たまに便利(正規表現のキャッシュを持たせてい る例: rr4r)

Slide 37

Slide 37 text

自動生成されたRの関数 Rust struct Counter { i: i32, } /// @export #[extendr] impl Counter { fn new() -> Self { Self { i: 0 } } fn count(&mut self) -> i32 { self.i = self.i + 1; self.i } }

Slide 38

Slide 38 text

自動生成されたRの関数 R #' @export Counter <- new.env(parent = emptyenv()) Counter$new <- function() .Call(wrap__Counter__new) Counter$count <- function() .Call(wrap__Counter__count, self) #' @rdname Counter #' @usage NULL #' @export `$.Counter` <- function (self, name) { func <- Counter[[name]]

Slide 39

Slide 39 text

実行結果 #> [1] 1 #> [1] 2 devtools::load_all(".") cnt <- Counter$new() cnt$count() cnt$count()

Slide 40

Slide 40 text

その他こまごました話 (時間があれば)

Slide 41

Slide 41 text

その他 文字列関連はlifetimeを意識しないと使えないの で初心者にはハードモード。数値計算系からはじ めるのがおすすめ。 Windowsのセットアップはやや面倒だけど、 GitHub Actionsでビルド済みのバイナリを配布し たりできるはず(調査中) CRANにはすでにextendrを使っているパッケージ も存在する

Slide 42

Slide 42 text

まとめ

Slide 43

Slide 43 text

まとめ extendrを使うとマクロの魔術でRustの関数から Rの関数を生成してくれる。 関数の引数の対応づけはRcppやcpp11と同じノ リ。慣れている人はわりとすぐに使えるはず。 ちなみに、今回はすべてRustのデータ型に変換 するタイプだったが、SEXPのまま扱うことも できる(ここはよく理解できていない) フィードバックお待ちしています!

Slide 44

Slide 44 text

r-wakalangにrustチャンネルをつくり ました

Slide 45

Slide 45 text

References extendr: https://github.com/extendr/extendr extendrのロゴはCC-BY-SA 4.0ライセンスで配布 されています: https://github.com/extendr/artwork.