如何从 NodeJS 中的 Rust FFI 函数 return 字符串值?
How to return string value from a Rust FFI function in NodeJS?
我想生成 6 个随机数,将它们推送到一个向量上,然后使用 rustc_serialize
将该向量编码为 JSON 字符串以供 NodeJS 使用。
extern crate rand;
extern crate rustc_serialize;
use rand::{OsRng, Rng};
use rustc_serialize::json::{self, Json, ToJson};
#[no_mangle]
pub extern "C" fn generate() -> String {
let choices: [u8; 6] = [1, 2, 3, 4, 5, 6];
let mut rand_vec: Vec<u8> = Vec::new();
let mut rng = match OsRng::new() {
Ok(t) => t,
Err(e) => panic!("Failed to create OsRng!, {}", e),
};
for _ in 0..5 {
rand_vec.push(*rng.choose(&choices).unwrap());
}
json::encode(&rand_vec).unwrap()
}
此代码被编译为库 generate_6_rand.dll
。我有一个单独的二进制文件用于测试此代码。
如果我运行
println!("{:?}", &json::encode(&rand_vec).unwrap());
输出:
"[5,4,3,4,1,3]" //as expected
然后我在 NodeJS 程序中使用我的 .dll
:
var ffi = require('ffi');
var path = require('path');
var lib = ffi.Library(path.join(__dirname,
'./ffi/generate_6_rand.dll'), {
generate: [ 'string', [ ]]
});
console.log(lib.generate());
测试
console.log(lib.generate())
输出:
��.�
它是 EcmaScript ArrayBuffer
吗?
console.log(new ArrayBuffer(lib.generate())
输出:
ArrayBuffer { byteLength: 0 }
它的原链属性是什么?
console.log(lib.generate().__proto__)
输出:
[String: '']
将代码更改为:
var ref = require('ref');
var ArrayType = require('ref-array');
var Int32Array = ArrayType(ref.types.int32);
var lib = ffi.Library(path.join(__dirname,
'../dice_lib/target/release/generate_6_rand.dll'), {
generate: [ Int32Array, [ ]]
});
console.log(new ArrayBuffer(lib.generate()));
输出:
ArrayBuffer { byteLength: 0 }
为什么 FFI 函数不像我期望的那样 return 一个 JSON 字符串?
感谢 Wesley Wiser 为我提供了有关 CString
的重要线索。我在 The Rust FFI Omnibus.
中找到了答案
我的预期 JSON 字符串的内存在我的 NodeJS 程序可以访问它之前被释放,无论我返回 JSON 字符串还是返回 CString
.
这是我基于那篇文章的解决方案。 对于其他新手程序员,请记住我的解决方案可能理想也可能不理想:
生锈
extern crate rand;
extern crate rustc_serialize;
extern crate libc;
use libc::c_char;
use rand::{OsRng, Rng};
use std::ffi::CString;
use rustc_serialize::json;
#[no_mangle]
pub extern "C" fn generate() -> *mut c_char {
let choices: [u8; 6] = [1, 2, 3, 4, 5, 6];
let mut rand_vec: Vec<u8> = Vec::new();
let mut rng = match OsRng::new() {
Ok(t) => t,
Err(e) => panic!("Failed to create OsRng!, {}", e),
};
for _ in 0..6 {
rand_vec.push(*rng.choose(&choices).unwrap());
}
let json_string = CString::new(json::encode(&rand_vec).unwrap()).unwrap();
json_string.into_raw()
}
#[no_mangle]
pub extern "C" fn free_memory(pointer: *mut c_char) {
unsafe {
if pointer.is_null() {
return;
}
CString::from_raw(pointer)
};
}
NodeJS
var ffi = require('ffi');
var path = require('path');
var lib = ffi.Library(path.join(__dirname,
'./ffi/generate_6_rand.dll'), {
generate: [ 'char *' , [ ]],
free_memory: ['void', ['char *']]
});
var json_string = lib.generate();
var save_json = JSON.parse(json_string.readCString());
console.log( json_string.readCString()); // Output: [6,1,6,4,1,4]
lib.free_memory(json_string);
console.log(json_string.readCString()); // Output: ��x�
我设置了两个 console.log
来显示释放前后的输出。
我想生成 6 个随机数,将它们推送到一个向量上,然后使用 rustc_serialize
将该向量编码为 JSON 字符串以供 NodeJS 使用。
extern crate rand;
extern crate rustc_serialize;
use rand::{OsRng, Rng};
use rustc_serialize::json::{self, Json, ToJson};
#[no_mangle]
pub extern "C" fn generate() -> String {
let choices: [u8; 6] = [1, 2, 3, 4, 5, 6];
let mut rand_vec: Vec<u8> = Vec::new();
let mut rng = match OsRng::new() {
Ok(t) => t,
Err(e) => panic!("Failed to create OsRng!, {}", e),
};
for _ in 0..5 {
rand_vec.push(*rng.choose(&choices).unwrap());
}
json::encode(&rand_vec).unwrap()
}
此代码被编译为库 generate_6_rand.dll
。我有一个单独的二进制文件用于测试此代码。
如果我运行
println!("{:?}", &json::encode(&rand_vec).unwrap());
输出:
"[5,4,3,4,1,3]" //as expected
然后我在 NodeJS 程序中使用我的 .dll
:
var ffi = require('ffi');
var path = require('path');
var lib = ffi.Library(path.join(__dirname,
'./ffi/generate_6_rand.dll'), {
generate: [ 'string', [ ]]
});
console.log(lib.generate());
测试
console.log(lib.generate())
输出:
��.�
它是 EcmaScript ArrayBuffer
吗?
console.log(new ArrayBuffer(lib.generate())
输出:
ArrayBuffer { byteLength: 0 }
它的原链属性是什么?
console.log(lib.generate().__proto__)
输出:
[String: '']
将代码更改为:
var ref = require('ref');
var ArrayType = require('ref-array');
var Int32Array = ArrayType(ref.types.int32);
var lib = ffi.Library(path.join(__dirname,
'../dice_lib/target/release/generate_6_rand.dll'), {
generate: [ Int32Array, [ ]]
});
console.log(new ArrayBuffer(lib.generate()));
输出:
ArrayBuffer { byteLength: 0 }
为什么 FFI 函数不像我期望的那样 return 一个 JSON 字符串?
感谢 Wesley Wiser 为我提供了有关 CString
的重要线索。我在 The Rust FFI Omnibus.
我的预期 JSON 字符串的内存在我的 NodeJS 程序可以访问它之前被释放,无论我返回 JSON 字符串还是返回 CString
.
这是我基于那篇文章的解决方案。 对于其他新手程序员,请记住我的解决方案可能理想也可能不理想:
生锈
extern crate rand;
extern crate rustc_serialize;
extern crate libc;
use libc::c_char;
use rand::{OsRng, Rng};
use std::ffi::CString;
use rustc_serialize::json;
#[no_mangle]
pub extern "C" fn generate() -> *mut c_char {
let choices: [u8; 6] = [1, 2, 3, 4, 5, 6];
let mut rand_vec: Vec<u8> = Vec::new();
let mut rng = match OsRng::new() {
Ok(t) => t,
Err(e) => panic!("Failed to create OsRng!, {}", e),
};
for _ in 0..6 {
rand_vec.push(*rng.choose(&choices).unwrap());
}
let json_string = CString::new(json::encode(&rand_vec).unwrap()).unwrap();
json_string.into_raw()
}
#[no_mangle]
pub extern "C" fn free_memory(pointer: *mut c_char) {
unsafe {
if pointer.is_null() {
return;
}
CString::from_raw(pointer)
};
}
NodeJS
var ffi = require('ffi');
var path = require('path');
var lib = ffi.Library(path.join(__dirname,
'./ffi/generate_6_rand.dll'), {
generate: [ 'char *' , [ ]],
free_memory: ['void', ['char *']]
});
var json_string = lib.generate();
var save_json = JSON.parse(json_string.readCString());
console.log( json_string.readCString()); // Output: [6,1,6,4,1,4]
lib.free_memory(json_string);
console.log(json_string.readCString()); // Output: ��x�
我设置了两个 console.log
来显示释放前后的输出。