rhaiscript / rhai

Rhai - An embedded scripting language for Rust.
https://crates.io/crates/rhai
Apache License 2.0
3.77k stars 178 forks source link

[Question] Is there a way to turn a value with a lifetime into a `Dynamic` #882

Closed sgpthomas closed 3 months ago

sgpthomas commented 4 months ago

I have some code that looks like this.

impl EmitSetup for RhaiSetupCtx {
    fn setup_rc<'a>(&self, emitter: Rc<RefCell<Emitter<'a>>>) -> EmitResult {
        let mut scope = rhai::Scope::new();
        scope.push("e", emitter);

        // do something with scope

        Ok(())
    }
}

I want some Rhai code to be able to access this Emitter<'a> struct, and I can't find a good way to do this. If I try and put it in a scope or use it in an argument with call_fn, I get a lifetime error essentially saying that constructing Dynamic from a Rc<RefCell<Emitter<'a>>> means that 'a has to last longer than 'static. Is there a way to get around this? Or can I not use any struct with a lifetime in a Rhai context?

If this example isn't clear enough, I'm happy to construct a self-contained example.

schungx commented 4 months ago

Thanks, your example is clear.

Rhai is sandboxed, meaning that it technically speaking should not be able to interfere with the external host, including keeping references into those hosts.

If Rhai is allowed to keep references into its containing host, it must then be bound by a lifetime itself (which should be the shortest lifetime of all the references that it keeps). Not only will this be difficult to handle, but the shortest lifetime of all your data may not be enough for most uses...

Also, allowing the Rhai engine to keep references into its container host is a circular reference, meaning that you must then separate the data structure containing the engine from the data structure containing the referenced data.

Therefore, the net result is that Rhai currently does not support keeping data with references.

It is possible, however, to make Rhai's evaluation API lifetime-aware... as you can see in Rhai's source that some key types (e.g. Scope and previously also GlobalRuntimeState) have generic parameters defaulted to 'static to prepare for this in the future. As it currently stands, it is not supported.

I'll mark this as an new feature request.

In the meantime, the only way to get referenced data into Rhai is through sharing.

Or you can consider this: https://rhai.rs/book/patterns/global-mutable-state.html

sgpthomas commented 4 months ago

Thanks for the detailed response. That all makes sense. A lifetime-aware evaluation API sounds like exactly what I need. I might poke around and see how hard this would be to implement if I get some spare time.

In the meantime, I found a solution to make Emitter not have a lifetime and things are working.

schungx commented 4 months ago

Thanks for the detailed response. That all makes sense. A lifetime-aware evaluation API sounds like exactly what I need. I might poke around and see how hard this would be to implement if I get some spare time.

In the meantime, I found a solution to make Emitter not have a lifetime and things are working.

Just out of curiosity, how did you get rid of the lifetime?

sgpthomas commented 4 months ago

Emitter had a field with Box<dyn Write + 'a>. This is for a library, and so I didn't want to change the external api. But I had some freedom with internal data structures. I changed the Box<dyn Write + 'a> to an intermediate buffer of Vec<u8> that we write to. And then we write out the Vec<u8> to the passed in Write after we've done our work.

schungx commented 4 months ago

You have to be ultra careful with such raw pointer casts though. Make sure you leave nothing behind your cleanup.

schungx commented 3 months ago

Closing this for now. Feel free to reopen if there are further issues.