28

在运行时使用 C,我可以:

  1. 创建函数的源代码,
  2. 调用 gcc 将其编译为 .so (Linux)(或使用 llvm 等),
  3. 加载.so,然后
  4. 调用函数。

在 Rust 中是否可能发生类似的事情?

特别是我想使用代数数据类型,所以使用 Rust 功能的 C 子集是不够的。

4

2 回答 2

22

还没有,正式的,虽然它至少应该是可能的,没有太多的黑客攻击。最大的障碍是这些库还没有任何动态加载的能力。这是使其工作的潜在策略(在 Rust 的传入分支上)。

  • 链接到 rustc crate 以编程方式驱动编译器。请注意,编译器不是线程安全的,因此一次只能运行一个进程内构建。
  • 用 标记您要调用的函数#[no_mangle]。这应该(我没有尝试过)产生一个未损坏的符号名称,因此很容易找到。
  • 创建到 dlopen/dlsym 的最小绑定
  • 找到函数指针并将其不安全地转换为 Rust 闭包类型(当前定义在 中sys::Closure)。
  • 调用闭包。

Rust 也有一个经过最低限度测试的 JIT 可以用于这种类型的事情,但它有一些主要的错误。

于 2013-01-22T23:57:22.677 回答
8

可能还有其他一些可能的解决方案,但是我最近使用了以下解决方案,并且它一直适用于我的特定要求。

希望它对您也有用,或者它可以为您提供更复杂和更强大的解决方案的起点。

main.rs

use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::process::Command;
use libloading::{Library, Symbol};

/// signature of function which should be called from plugin
type AddFunc = unsafe fn(isize, isize) -> isize;

/// Create a plugin file at runtime which will be converted to shared library
fn write_file()  -> std::io::Result<()> {
    let mut file = File::create("plugin.rs")?;
    file.write_all(b"fn main() {\n")?;
    file.write_all(b"\t#[no_mangle]\n")?;
    file.write_all(b"\tpub extern \"C\" fn add(a: isize, b: isize) -> isize {\n")?;
    file.write_all(b"\t\ta + b\n")?;
    file.write_all(b"\t}\n")?;
    file.write_all(b"}\n")?;
    Ok(())
}
/// compile plugin code file to shared library
/// todo 1) should allow to pass file path. 
///      2) should return path to generated shared library
fn compile_file() {
    let mut compile_file = Command::new("cmd");
    compile_file.args(&["/C", "rustc", "--crate-type", "cdylib", "plugin.rs"]).status().expect("process failed to execute");
}
/// call function from shared library
/// todo suffix should be selected based on OS.
fn call_plugin(a: isize, b: isize) -> isize {
    let lib = Library::new("plugin.dll").unwrap();
    unsafe {
        let func: Symbol<AddFunc> = lib.get(b"add").unwrap();
        let answer = func(a, b);
        answer
    }
}
fn main(){
    let args: Vec<String> = env::args().collect();
    if args.len() == 3 {
        write_file();
        compile_file();
        /// get argument from commandline to pass to function
        let a: isize = args[1].trim().parse().expect("number required");
        let b: isize = args[2].trim().parse().expect("number required");
        println!("{}+{}:{}",a,b,call_plugin(a,b));
    }
    else {
        println!("USAGE: main.exe NUM NUM");
    }
}

货运.toml

[package]
name = "runtime_plugin"
version = "0.1.0"
authors = ["Manthan R Tilva"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
libloading = "0.5.2"

您也可以在github中找到此代码

于 2019-11-08T10:10:58.623 回答