pub struct Lua { /* private fields */ }Expand description
Primary owned embedding handle.
Lua is intentionally cheap to clone and single-threaded. State access is
borrowed at the embedding boundary only; opcode dispatch still runs with
direct &mut LuaState access. Captured Rust callbacks will need a call-path
adapter that releases this boundary borrow before invoking user code.
Implementations§
Source§impl Lua
impl Lua
Sourcepub fn new() -> Self
pub fn new() -> Self
Create a Lua runtime with parser and standard libraries installed.
Examples found in repository?
65fn main() -> Result<()> {
66 let lua = Lua::new();
67 let mut world = World::default();
68
69 let count: i64 = lua.scope(|s| {
70 let world_ud = s.create_userdata_ref_mut(&lua, &mut world)?;
71 lua.globals().set("world", &world_ud)?;
72
73 lua.load(
74 r#"
75 local a = world:spawn()
76 local b = world:spawn()
77
78 -- Direct mutation through a live sub-reference.
79 local pa = world:position(a)
80 pa.x, pa.y = 10, 20
81
82 local pb = world:position(b)
83 pb.x = pa.x + 5 -- reading pa here re-borrows; both are short-lived
84
85 -- Stash one to prove it dies with the scope.
86 escaped = world:position(a)
87
88 return world:count()
89 "#,
90 )
91 .eval()
92 })?;
93
94 println!("spawned {count} entities");
95 // The mutations are visible to Rust after the scope returns.
96 for (i, e) in world.entities.iter().enumerate() {
97 println!("entity {i} = ({}, {})", e.x, e.y);
98 }
99
100 // The handle the script stashed on `escaped` is now invalid. Reading a
101 // field raises a Lua error rather than touching the released `&mut World`.
102 // We surface the message through Lua's own `pcall` + `tostring`, since the
103 // error payload is a Lua value.
104 let (ok, msg): (bool, String) = lua
105 .load("local ok, e = pcall(function() return escaped.x end); return ok, tostring(e)")
106 .eval()?;
107 assert!(!ok, "stashed handle should be unusable after the scope");
108 println!("post-scope use of a stashed handle -> {msg}");
109
110 Ok(())
111}Sourcepub fn with_hooks(hooks: HostHooks) -> Result<Self>
pub fn with_hooks(hooks: HostHooks) -> Result<Self>
Create a Lua runtime with the supplied host capabilities.
Sourcepub fn load(&self, source: impl AsRef<[u8]>) -> Chunk
pub fn load(&self, source: impl AsRef<[u8]>) -> Chunk
Load a Lua source chunk.
Examples found in repository?
65fn main() -> Result<()> {
66 let lua = Lua::new();
67 let mut world = World::default();
68
69 let count: i64 = lua.scope(|s| {
70 let world_ud = s.create_userdata_ref_mut(&lua, &mut world)?;
71 lua.globals().set("world", &world_ud)?;
72
73 lua.load(
74 r#"
75 local a = world:spawn()
76 local b = world:spawn()
77
78 -- Direct mutation through a live sub-reference.
79 local pa = world:position(a)
80 pa.x, pa.y = 10, 20
81
82 local pb = world:position(b)
83 pb.x = pa.x + 5 -- reading pa here re-borrows; both are short-lived
84
85 -- Stash one to prove it dies with the scope.
86 escaped = world:position(a)
87
88 return world:count()
89 "#,
90 )
91 .eval()
92 })?;
93
94 println!("spawned {count} entities");
95 // The mutations are visible to Rust after the scope returns.
96 for (i, e) in world.entities.iter().enumerate() {
97 println!("entity {i} = ({}, {})", e.x, e.y);
98 }
99
100 // The handle the script stashed on `escaped` is now invalid. Reading a
101 // field raises a Lua error rather than touching the released `&mut World`.
102 // We surface the message through Lua's own `pcall` + `tostring`, since the
103 // error payload is a Lua value.
104 let (ok, msg): (bool, String) = lua
105 .load("local ok, e = pcall(function() return escaped.x end); return ok, tostring(e)")
106 .eval()?;
107 assert!(!ok, "stashed handle should be unusable after the scope");
108 println!("post-scope use of a stashed handle -> {msg}");
109
110 Ok(())
111}Sourcepub fn globals(&self) -> Table
pub fn globals(&self) -> Table
Return the global environment table.
Examples found in repository?
65fn main() -> Result<()> {
66 let lua = Lua::new();
67 let mut world = World::default();
68
69 let count: i64 = lua.scope(|s| {
70 let world_ud = s.create_userdata_ref_mut(&lua, &mut world)?;
71 lua.globals().set("world", &world_ud)?;
72
73 lua.load(
74 r#"
75 local a = world:spawn()
76 local b = world:spawn()
77
78 -- Direct mutation through a live sub-reference.
79 local pa = world:position(a)
80 pa.x, pa.y = 10, 20
81
82 local pb = world:position(b)
83 pb.x = pa.x + 5 -- reading pa here re-borrows; both are short-lived
84
85 -- Stash one to prove it dies with the scope.
86 escaped = world:position(a)
87
88 return world:count()
89 "#,
90 )
91 .eval()
92 })?;
93
94 println!("spawned {count} entities");
95 // The mutations are visible to Rust after the scope returns.
96 for (i, e) in world.entities.iter().enumerate() {
97 println!("entity {i} = ({}, {})", e.x, e.y);
98 }
99
100 // The handle the script stashed on `escaped` is now invalid. Reading a
101 // field raises a Lua error rather than touching the released `&mut World`.
102 // We surface the message through Lua's own `pcall` + `tostring`, since the
103 // error payload is a Lua value.
104 let (ok, msg): (bool, String) = lua
105 .load("local ok, e = pcall(function() return escaped.x end); return ok, tostring(e)")
106 .eval()?;
107 assert!(!ok, "stashed handle should be unusable after the scope");
108 println!("post-scope use of a stashed handle -> {msg}");
109
110 Ok(())
111}Sourcepub fn create_table(&self) -> Result<Table>
pub fn create_table(&self) -> Result<Table>
Create a new empty table.
Sourcepub fn create_string(&self, bytes: impl AsRef<[u8]>) -> Result<LuaString>
pub fn create_string(&self, bytes: impl AsRef<[u8]>) -> Result<LuaString>
Create a new Lua string from bytes.
pub fn create_function<A, R, F>(&self, func: F) -> Result<Function>
pub fn create_function_mut<A, R, F>(&self, func: F) -> Result<Function>where
A: FromLuaMulti + 'static,
R: IntoLuaMulti + 'static,
F: FnMut(&Lua, A) -> Result<R> + 'static,
pub fn create_userdata<T>(&self, data: T) -> Result<AnyUserData>where
T: UserData,
Sourcepub fn scope<F, R>(&self, f: F) -> Result<R>
pub fn scope<F, R>(&self, f: F) -> Result<R>
Run f with a fresh Scope; any AnyUserData created via the
scope is invalidated when f returns, so leaked references fail
cleanly instead of using-after-the-borrow-ended.
use lua_rs_runtime::{Lua, UserData, UserDataMethods};
struct Counter { value: i64 }
impl UserData for Counter {
fn add_methods<M: UserDataMethods<Self>>(methods: &mut M) {
methods.add_method_mut("inc", |_lua, this, delta: i64| {
this.value += delta;
Ok(this.value)
});
}
}
let lua = Lua::new();
let mut counter = Counter { value: 0 };
lua.scope(|scope| {
let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
lua.globals().set("c", &ud)?;
lua.load("c:inc(5); c:inc(7)").exec()
}).unwrap();
assert_eq!(counter.value, 12);
// The script can stash the userdata on a global and try to use it
// later, but the call cleanly errors instead of touching the
// dropped `&mut counter`:
lua.scope(|scope| {
let ud = scope.create_userdata_ref_mut(&lua, &mut counter)?;
lua.globals().set("leaked", &ud)
}).unwrap();
assert!(lua.load("leaked:inc(1)").exec().is_err());Examples found in repository?
65fn main() -> Result<()> {
66 let lua = Lua::new();
67 let mut world = World::default();
68
69 let count: i64 = lua.scope(|s| {
70 let world_ud = s.create_userdata_ref_mut(&lua, &mut world)?;
71 lua.globals().set("world", &world_ud)?;
72
73 lua.load(
74 r#"
75 local a = world:spawn()
76 local b = world:spawn()
77
78 -- Direct mutation through a live sub-reference.
79 local pa = world:position(a)
80 pa.x, pa.y = 10, 20
81
82 local pb = world:position(b)
83 pb.x = pa.x + 5 -- reading pa here re-borrows; both are short-lived
84
85 -- Stash one to prove it dies with the scope.
86 escaped = world:position(a)
87
88 return world:count()
89 "#,
90 )
91 .eval()
92 })?;
93
94 println!("spawned {count} entities");
95 // The mutations are visible to Rust after the scope returns.
96 for (i, e) in world.entities.iter().enumerate() {
97 println!("entity {i} = ({}, {})", e.x, e.y);
98 }
99
100 // The handle the script stashed on `escaped` is now invalid. Reading a
101 // field raises a Lua error rather than touching the released `&mut World`.
102 // We surface the message through Lua's own `pcall` + `tostring`, since the
103 // error payload is a Lua value.
104 let (ok, msg): (bool, String) = lua
105 .load("local ok, e = pcall(function() return escaped.x end); return ok, tostring(e)")
106 .eval()?;
107 assert!(!ok, "stashed handle should be unusable after the scope");
108 println!("post-scope use of a stashed handle -> {msg}");
109
110 Ok(())
111}Sourcepub fn gc_collect(&self)
pub fn gc_collect(&self)
Run a full garbage-collection cycle.
Source§impl Lua
impl Lua
Sourcepub fn sandboxed(config: SandboxConfig) -> Result<(Self, Sandbox)>
pub fn sandboxed(config: SandboxConfig) -> Result<(Self, Sandbox)>
Create a Lua runtime with no host capabilities (no file, process, or
dynamic-library hooks), the configured globals stripped, and an
instruction/memory budget installed. Returns the runtime and a
Sandbox handle for inspecting and resetting the budget.
Sourcepub fn install_sandbox(&self, config: SandboxConfig) -> Result<Sandbox>
pub fn install_sandbox(&self, config: SandboxConfig) -> Result<Sandbox>
Apply sandbox limits to this runtime: strip the configured globals and,
if any runtime limit is set, install the runtime-wide budget. The budget
lives in the shared GlobalState and is enforced natively in the VM on
every thread, so code inside coroutines is metered too. Use this when
you want to grant some host capabilities (build the Lua with selected
HostHooks) but still bound execution.