How to read _ENV after script execution?
singalen opened this issue · 2 comments
We have Chunk::set_environment()
to set _ENV
.
(Initially I expected lua_ctx.globals().set()
to work, but apparently it's not the way).
Now, I'd like to know all the global variables that user has set, so I want to read _ENV
back, but apparently there is no way? lua_ctx.globals().get("_ENV")
returns an empty Table
.
What am I missing? Thank you.
Hi,
The way _ENV works in Lua definitely causes confusion - it's not exactly the same as global variables.
As a quick summary, loading a chunk of code like x = 3*y
the Chunk
is a lot like defining a function where any references to non-locals are rewritten to refer to _ENV
:
function()
_ENV.x = 3*_ENV.y
end
_ENV
by default is an upvalue which points to the same table as is returned by Context::globals()
, but you can point that to anything else with Chunk::set_environment()
. That doesn't affect Context::globals()
at all, just what that chunk/function sees when executing.
It is possible in the low level Lua API to read back the upvalues from a chunk, but that isn't currently exposed in rlua. However, you can just keep a reference to it and read it after executing the chunk:
fn main() {
Lua::new().context(|lua| {
// Load some Lua code
let chunk = lua.load(r#"
print(x)
x = 3
y = 7
"#);
// Set up a new environment with some predefined data
let new_env = lua.create_table()?;
new_env.set("x", 1)?;
let print_function: Function = lua.globals().get("print")?;
// The code uses print()
new_env.set("print", print_function)?;
// The code will execute with new_env as the _ENV value,
// which acts as the globals in that chunk.
let chunk = chunk.set_environment(new_env.clone())?;
// Actually parse and run it. This should print "1"
chunk.exec()?;
let new_x: i32 = new_env.get("x")?;
let new_y: i32 = new_env.get("y")?;
println!("After executing, x={} y={}", new_x, new_y);
Ok::<(), rlua::Error>(())
}).unwrap();
}
If you need to keep a reference to the environment for longer than one Lua::context()
call then the normal way to do that is to save it in the registry using Context::create_registry_value()
.
Great, thank you! I expected Table
to be a value type, not "reference". But I should have guessed by the fact that it requires a lifetime.