LoginSignup
3
1

More than 5 years have passed since last update.

Rust と Node.js の文字列やり取り、そして Callback

Last updated at Posted at 2019-02-05

環境

rustc 1.32.0 (9fda7c223 2019-01-16)
Node.js 10.15.1

Rust から Node.js に Callback したい

というわけで、Cargo.toml です。cdylibってやると動的ライブラリができます。Windows では dll ってやつですね。

Cargo.toml
[package]
name = "callback"
version = "0.1.0"
authors = ["benki"]
edition = "2018"

[dependencies]

[lib]
name = "callback"
crate-type = ["cdylib"]

続いて、lib.rs です。rust_funcっていう関数は第一引数に C の文字列へのポインタ、第二引数にコールバック関数へのポインタを受け取るようになってます。

rust_func関数の1行目で Node.js の世界からやってきた C 文字列へのポインタを Rust の世界の文字列に変換して、2行目でdbg!マクロでその内容を表示しています。

その後の処理はカレントディレクトリにあるファイルのパスをコールバック関数の引数に渡しています。Rust の世界の文字列は NULL 終端文字がないので、そのまま Node.js の世界に渡すと結果がおかしなことになります。なので、path.push('\0');で NULL 終端文字を追加する必要があります。(もうちょっとうまいことできる方法があったら教えてください)なので、std::ffi::CStringで C の世界の文字列に変換してます。(tatsuya6502さんにコメントで教えてもらいました)

lib.rs
use std::fs;
use std::os::raw::c_char;
use std::ffi::{CStr, CString};

type CallBack = fn(*const c_char);

#[no_mangle]
pub fn rust_func(string: *const c_char, cb: CallBack) {
    let str_from_node = unsafe { CStr::from_ptr(string).to_str().unwrap() };
    dbg!(str_from_node);

    fs::read_dir(".").unwrap()
        .for_each(|r| {
            if let Ok(r) = r {
                // let mut path = r.path().to_string_lossy().to_owned().to_string();
                // path.push('\0');
                // cb(path.as_ptr() as *const c_char);
                if let Ok(path) = CString::new(r.path().to_string_lossy().as_bytes()) {
                    cb(path.as_ptr() as *const c_char);
                }
            }
        });
}

最後に Node.js 側のコードです。

index.js
let ffi = require("ffi");
let util = require("util");

let lib = ffi.Library("<path to callback.dll or so>",
    {"rust_func": // Rust の世界の関数の名前
        ["void", // 戻り値
            ["string", // 第一引数
            "pointer"] // 第二引数(コールバック関数へのポインタ)
        ]
    }
);

let callback = ffi.Callback("void", ["string"], (path) => {
    console.log(path);
});

let rust_func = util.promisify(lib.rust_func.async);

(async () => {
    await rust_func("Hello from Node!", callback);    
})().catch(e => {
    console.log(e);
});
3
1
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
1