This crate provides a macro, dymod!
, which allows you to specify a Rust module which will by dynamically loaded and hotswapped in debug mode, but statically linked in release mode.
Note that this is very much experimental. The current version of this crate is very opinionated about how you structure your dynamic code. Hopefully this will be relaxed a little in future.
Your dynamically loaded code should be placed in its own sub-crate under your main crate:
mycrate/
Cargo.toml
src/
main.rs
subcrate/
Cargo.toml
src/
lib.rs
Your subcrate must also be compiled as a dylib, so in your subcrate/Cargo.toml
add:
toml
[lib]
crate-type = ["dylib"]
Now you need to add the code that you want to hotswap. Any functions should be pub
and #[no_mangle]
. See the Limitations section below for what kind of code you can put here.
```rust // subcrate/src/lib.rs
pub fn count_sheep(sheep: u32) -> &'static str { match sheep { 0 => "None", 1 => "One", 2 => "Two", 3 => "Many", _ => "Lots" } } ```
Finally, use the dymod!
macro to specify your module, along with the functions that are dynamically available from it.
```rust // mycrate/src/main.rs
extern crate dymod;
dymod! { #[path = "../subcrate/src/lib.rs"] pub mod subcrate { fn count_sheep(sheep: u32) -> &'static str; } }
fn main() { asserteq!(subcrate::countsheep(3), "Many"); loop { // You can now edit the countsheep function and see // the results change while this code is running. println!("{}", subcrate::countsheep(3)); } } ```
This is really, really unsafe! But only in debug mode. In release mode, the module you specify is linked statically as if it was a regular module, and there should be no safety concerns.
Here is a partial list of what can go wrong in debug mode:
So as described above, you cannot rely on hotswapping to work if you change struct definitions while your code is running.
You also cannot reliably take ownership of heap allocated data from one side of the boundary to the other.
Generic functions will not work either.
This is again, just a partial list. There really are quite a lot of constraints on what you can do.
I suppose we'll see!
Here are some examples of code that should work and would be useful to hotswap:
```rust
pub fn game_update(state: &mut GameState) { // Modify game state. // No need to return anything problematic. unimplemented!() }
pub fn animatefromto(pointa: [f32; 2], pointb: [f32; 2], time: f32) -> [f32; 2] { // Returns only stack-allocated values and so is safe. // Specific kind of animation can be changed on the fly. unimplemented!() }
pub fn get_configuration() -> Config { // Again, returns only stack-allocated values. // Allows changing some configuration while running. Config { ... } } ```