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

fad*_*bee 24 algebraic-data-types rust

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

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

在Rust中类似的东西可能吗?

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

brs*_*son 19

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

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

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

  • 这个答案很老了.平均时间变化了多少? (13认同)

Man*_*lva 5

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

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

主文件

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

Cargo.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"
Run Code Online (Sandbox Code Playgroud)

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

  • OP 特别说:*特别是我想使用代数数据类型,所以**使用 Rust 功能的 C 子集是不够的***(强调我的)。这个答案使用 C ABI 构建了一个带有函数的 cdylib。 (2认同)