是否可以在运行时生成并执行Rust代码?

时间:2013-01-22 13:26:01

标签: c rust algebraic-data-types

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

  1. 创建函数的源代码
  2. 调用gcc将其编译为.so(Linux)(或使用llvm等),
  3. 加载.so和
  4. 调用该函数。
  5. 在Rust中可能有类似的事情吗?

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

2 个答案:

答案 0 :(得分:14)

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

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

Rust还有一个经过最低限度测试的JIT,可用于此类事情,但它有一些重大错误。

答案 1 :(得分:2)

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

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

main.rs

Prelude> :{
Prelude| fact :: Int -> Int
Prelude| fact n = product [1..n]
Prelude| :}

Cargo.toml

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");
    }
}

您还可以在github

中找到此代码