#debug-mode #dynamic #linker #static #dylib #debugging #hotswapping

dymod

一个宏,允许在调试模式下热插拔模块中的代码,但在发布模式下安全地静态链接

4个版本 (破坏性更新)

0.4.0 2023年7月22日
0.3.0 2020年12月23日
0.2.0 2020年1月18日
0.1.0 2018年2月22日

#407 in 游戏开发

每月 22 次下载

CC0 许可证

17KB
137

dymod

Build Status Crates.io Docs.rs

这个crate提供了一个宏dymod!,允许你指定一个Rust模块,该模块将在调试模式下动态加载和热插拔,但在发布模式下静态链接。

此crate的当前版本对如何组织你的动态代码有很强的意见。希望未来可以稍微放宽一些。

操作系统兼容性

这个crate已经在macOS (10.14.5),Ubuntu Linux (18.04.1),和Windows 10 (1903) 上进行了测试。但是,这是一个有点奇怪的crate,所以我不奇怪它会在一些其他操作系统上失败。告诉我吧!

用法

你的动态加载代码应该放在你主crate下的自己的子crate中

mycrate/
  Cargo.toml
  src/
    main.rs
  subcrate/
    Cargo.toml
    src/
      lib.rs

你的子crate还必须编译为dylib,所以在你的subcrate/Cargo.toml中添加

[lib]
crate-type = ["dylib"]

现在你需要添加你想要热插拔的代码。任何函数应该是 pub extern "C"#[no_mangle]

// subcrate/src/lib.rs
#[no_mangle]
pub extern "C" fn count_sheep(sheep: u32) -> &'static str {
    match sheep {
        0 => "None",
        1 => "One",
        2 => "Two",
        3 => "Many",
        _ => "Lots"
    }
}

最后,使用dymod!宏指定你的模块,以及从中动态可用的函数。

// src/main.rs
use dymod::dymod;

dymod! {
    #[path = "../subcrate/src/lib.rs"]
    pub mod subcrate {
        fn count_sheep(sheep: u32) -> &'static str;
    }
}

fn main() {
    assert_eq!(subcrate::count_sheep(3), "Many");
    loop {
        // You can now edit the count_sheep function,
        // recompile `subcrate`, and see the result change
        // while this code is running.
        println!("{}", subcrate::count_sheep(3));
    }
}

安全性

在发布模式下,你指定的模块被静态链接,就像是一个普通模块一样,应该没有安全问题。

然而,在调试模式下,当代码正在动态链接时,有很多事情可能会出错。可能会意外触发panic,或者产生未定义的行为。

以下是调试模式下可能会出错的列表的一部分

  • 如果你在dylib热插拔时持有dylib拥有的数据,你将得到未定义的行为。
  • 除非两个crate都使用系统分配器(幸运的是,自从Rust 1.32.0以来就是默认的),否则释放由另一个crate分配的数据将导致段错误。
  • 如果您在边界两侧更改结构体的定义,可能会出现未定义的行为。(这包括添加或删除枚举变体。)
  • 如果您在dymod!宏中指定了错误的函数签名,您将得到未定义的行为。

由于这些限制,建议您使用少量动态函数,并传递不太可能发生很大变化的类型。例如,在最简单的情况下

use dymod::dymod;

dymod! {
    #[path = "../subcrate/src/lib.rs"]
    pub mod subcrate {
        fn update_application_state(state: &mut ApplicationState);
    }
}

上述函数将为您提供在运行时调整任何应用程序状态的灵活性,但接口足够简单,易于维护。

手动重新加载

默认情况下,启用了auto-reload功能,当动态库发生变化时(在您尝试调用其函数时),它会重新加载。

如果您希望自行处理重新加载,可以禁用此功能(--no-default-features)并使用dymod模块的reload()函数重新加载。

出于同样的原因,目前无法在您的dymod模块中定义名为reload的函数。

依赖关系

~195KB