Rust 是动态类型语言, 如果有局部代码想要独自编译再加载, 就须要通过 link 来解决,
先把一个模块打包成 dynamic library, 而后运行的时候再来调用.
在 Windows 里边是 *.dll
文件, Linux 里是 *.so
文件, macos 是 *.dylib
.
其余还有更小众的操作系统, 可能还有不同的后缀 …
我这边对应的零碎是 macos.
首先独自编译的局部, Rust 文档给出了比拟多的品种, 看文档,
https://doc.rust-lang.org/ref…
我的场景用到的是 dylib
或者 cdylib
的计划, Cargo.toml
指定一下配置就好.
我试了一下, 两者对我来说都是能够的, cdylib
是对 C 的反对, 我临时用不到.
而后加载 dynamic library 的局部我间接用这个库了,
https://docs.rs/libloading/0….
然而须要留神的是, 加载的过程传参比拟麻烦, 简单构造会提醒 “ffi unsafe”.
具体起因没了解, 大略是跟内存布局无关, 我看传递的时候有时候是用的指针,
间接 usize 或者 bool 是能够间接传的, 然而 String 和 &str 传不了,
网上给的计划是用 CString 和 CStr 转换这边有例子,
https://doc.rust-lang.org/std…
此外就是 externs 写法的细节了, 我抄了写例子, 而后修 bug, 最终失去:
#[no_mangle]
pub unsafe extern "C" fn read_file(name_a: *const c_char) -> *mut c_char {let name = CStr::from_ptr(name_a).to_str().unwrap();
let task = fs::read_to_string(&name);
match task {Ok(s) => {let a = CString::new(s).unwrap();
CString::into_raw(a)
}
Err(e) => {panic!("Failed to read file {:?}: {}", name, e)
}
}
}
而后对应调用的局部是:
fn main() {let u = call_dynamic();
println!("Hello, world! {:?}", u);
}
fn call_dynamic() -> Result<String, Box<dyn std::error::Error>> {
unsafe {
let lib = libloading::Library::new("/Users/chen/repo/calcit-lang/std/target/release/libcalcit_std.dylib",)?;
let func: libloading::Symbol<unsafe extern "C" fn(name_a: *const c_char) -> *mut c_char> =
lib.get(b"read_file")?;
let a = CString::new("/Users/chen/repo/gist/rs-demo/Cargo.toml").expect("should not fail");
let c_name = a.as_ptr(); // <-- 标记行 A
let ret = CStr::from_ptr(func(c_name)).to_str().unwrap();
Ok(ret.to_owned())
}
}
两头遇到一些坑, 导致后果传递参数比拟长时间只能失去空字符串,
大抵是两个问题吧, 一个是 ” 标记行 A ” 的地位, 须要定义成变量能力失常,
刚开始我写在同一行, 调试始终拿不到内容, 最初参考网上的例子调整, 就好了,
看某个文章提到起因说这是为了给 a
一个独自的援用.(起源丢了)
另一个是从 dynamic library 返回的数据, 我原本用的 *const char
,
也是长时间读到空数据, 最初参考某个 FFI 的例子, 改成了 *mut char
随后胜利,
https://stackoverflow.com/a/4…
仍然不确定是怎么回事, 依稀翻到一个评论说这样为了让数据停留在堆上,
如果是前者, dynamic library 函数调用实现之后数据请跨了, 就读不到.(起源也丢了)
总之依照上边的写法, 编译后就能失去 macos 上的 *.dylib
文件, 动静加载.
首次应用, 没搞懂的细节还是挺多的, 包含后续怎么散发, 怎么跨平台, 都没有教训.
后续要停顿还是提前留笔记.