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

是否可以在运行时生成并执行Rust代码?,rust,algebraic-data-types,Rust,Algebraic Data Types,使用C,在运行时,我可以: 创建函数的源代码 调用gcc将其编译为.so(Linux)(或使用llvm等) 加载。那么,和 调用函数 在锈病中也可能发生类似的情况吗 特别是我想使用代数数据类型,所以使用Rust特性的C子集是不够的。官方还没有,但至少在不太多黑客攻击的情况下应该是可能的。最大的障碍是这些库还没有进行动态加载的能力。 这里有一个潜在的策略使它工作(在Rust的传入分支上) 链接到rustc板条箱以编程方式驱动编译器。请注意,编译器不是线程安全的,因此一次只能运行一个进程内构建 用

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

  • 创建函数的源代码
  • 调用gcc将其编译为.so(Linux)(或使用llvm等)
  • 加载。那么,和
  • 调用函数
  • 在锈病中也可能发生类似的情况吗


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

    官方还没有,但至少在不太多黑客攻击的情况下应该是可能的。最大的障碍是这些库还没有进行动态加载的能力。 这里有一个潜在的策略使它工作(在Rust的传入分支上)

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

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

    可能还有其他可能的解决方案,但是,我最近使用了下面的解决方案,它对于我的特定需求一直运行良好

    希望它也能对您有所帮助,或者它能为您提供一个起点,帮助您找到更复杂、更健壮的解决方案

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

    您也可以在

    中找到此代码。此答案非常古老。同时情况发生了多大的变化?OP特别指出:我特别想使用代数数据类型,因此使用Rust特征的C子集是不够的(我的重点)。这个答案构建了一个cdylib,其中包含使用C ABI的函数。
    [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"