scope_world/
scope_world.rs1use lua_rs_runtime::{AnyUserData, Lua, Result, UserData, UserDataMethods};
18
19#[derive(Default)]
21struct Position {
22 x: f64,
23 y: f64,
24}
25
26impl UserData for Position {
27 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
28 m.add_field_method_get("x", |_, this| Ok(this.x));
29 m.add_field_method_get("y", |_, this| Ok(this.y));
30 m.add_field_method_set("x", |_, this, v: f64| {
31 this.x = v;
32 Ok(())
33 });
34 m.add_field_method_set("y", |_, this, v: f64| {
35 this.y = v;
36 Ok(())
37 });
38 }
39}
40
41#[derive(Default)]
43struct World {
44 entities: Vec<Position>,
45}
46
47impl UserData for World {
48 fn add_methods<M: UserDataMethods<Self>>(m: &mut M) {
49 m.add_method_mut("spawn", |_, this, ()| {
50 this.entities.push(Position::default());
51 Ok((this.entities.len() - 1) as i64)
52 });
53 m.add_method("count", |_, this, ()| Ok(this.entities.len() as i64));
54
55 m.add_function("position", |lua, (this, idx): (AnyUserData, i64)| {
59 let i = idx as usize;
60 this.delegate::<World, Position, _>(lua, move |w| &mut w.entities[i])
61 });
62 }
63}
64
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 for (i, e) in world.entities.iter().enumerate() {
97 println!("entity {i} = ({}, {})", e.x, e.y);
98 }
99
100 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}