luars/lua_value/userdata_trait.rs
1// Trait-based Userdata system for Lua-rs
2//
3// Instead of using Lua's traditional metatable-based approach for userdata access,
4// we leverage Rust's trait system for direct, type-safe dispatch of field access,
5// method calls, and metamethods.
6//
7// Key design principles:
8// 1. Trait-based dispatch (no metatable lookup for known operations)
9// 2. Auto-derive from Rust structs via `#[derive(LuaUserData)]`
10// 3. Automatic metamethod generation from Rust trait impls (Display → __tostring, Ord → __lt, etc.)
11// 4. Backward compatibility via `as_any()` downcasting
12// 5. Metatables still work as fallback for Lua-level customization
13
14use std::any::Any;
15use std::fmt;
16
17use crate::lua_vm::CFunction;
18
19/// Intermediate value type for userdata field/method returns.
20///
21/// Since `LuaValue` requires GC-allocated strings, trait methods return `UdValue`
22/// which the VM converts to proper `LuaValue` (interning strings as needed).
23pub enum UdValue {
24 Nil,
25 Boolean(bool),
26 Integer(i64),
27 Number(f64),
28 /// A Rust string — will be interned by the VM when converting to LuaValue
29 Str(String),
30 /// A light C function — used for returning methods from `get_field`
31 Function(CFunction),
32 /// Borrowed reference to another userdata's inner value (as operand in
33 /// arithmetic/comparison). Valid only during the trait method call.
34 /// Use [`UdValue::as_userdata_ref`] to safely downcast.
35 UserdataRef(*const dyn Any),
36 /// Owned userdata value (as return from arithmetic trait methods).
37 /// The VM allocates this as a new GC-managed userdata.
38 UserdataOwned(Box<dyn UserDataTrait>),
39}
40
41impl Clone for UdValue {
42 fn clone(&self) -> Self {
43 match self {
44 UdValue::Nil => UdValue::Nil,
45 UdValue::Boolean(b) => UdValue::Boolean(*b),
46 UdValue::Integer(i) => UdValue::Integer(*i),
47 UdValue::Number(n) => UdValue::Number(*n),
48 UdValue::Str(s) => UdValue::Str(s.clone()),
49 UdValue::Function(f) => UdValue::Function(*f),
50 UdValue::UserdataRef(p) => UdValue::UserdataRef(*p),
51 UdValue::UserdataOwned(_) => {
52 // Cannot clone owned userdata — return Nil as fallback.
53 // This should not happen in practice; the VM consumes owned
54 // userdata immediately.
55 UdValue::Nil
56 }
57 }
58 }
59}
60
61impl UdValue {
62 #[inline]
63 pub fn is_nil(&self) -> bool {
64 matches!(self, UdValue::Nil)
65 }
66
67 /// Try to downcast a `UserdataRef` operand to a concrete type.
68 ///
69 /// Returns `Some(&T)` if the operand is a userdata of type `T`.
70 /// This is the primary way to access the other operand in arithmetic
71 /// trait methods when both operands are userdata.
72 ///
73 /// # Safety
74 /// The returned reference borrows from the GC-managed userdata on the
75 /// Lua stack. It is valid for the duration of the trait method call.
76 ///
77 /// # Example (generated by derive macro)
78 /// ```ignore
79 /// fn lua_add(&self, other: &UdValue) -> Option<UdValue> {
80 /// if let Some(o) = other.as_userdata_ref::<Vec2>() {
81 /// Some(UdValue::from_userdata(Vec2 { x: self.x + o.x, y: self.y + o.y }))
82 /// } else {
83 /// None
84 /// }
85 /// }
86 /// ```
87 #[inline]
88 pub fn as_userdata_ref<T: 'static>(&self) -> Option<&T> {
89 match self {
90 UdValue::UserdataRef(ptr) => {
91 // SAFETY: The pointer originates from a GC-managed userdata that
92 // is alive on the Lua stack during this call. The VM guarantees
93 // the pointer remains valid for the duration of the trait method.
94 let any_ref: &dyn Any = unsafe { &**ptr };
95 any_ref.downcast_ref::<T>()
96 }
97 _ => None,
98 }
99 }
100
101 /// Wrap a value that implements `UserDataTrait` into an owned `UdValue`.
102 ///
103 /// Use this as the return value from arithmetic trait methods when the
104 /// result is a new userdata (e.g., `Vec2 + Vec2 → Vec2`).
105 #[inline]
106 pub fn from_userdata<T: UserDataTrait>(value: T) -> Self {
107 UdValue::UserdataOwned(Box::new(value))
108 }
109}
110
111/// Describes how a userdata type can be accessed from Lua.
112///
113/// This trait provides rich, typed access to struct fields, methods, and standard
114/// operations. The `#[derive(LuaUserData)]` macro auto-implements this trait by
115/// exposing public fields for structs, or by creating a fieldless userdata facade for
116/// enums and tuple/unit structs. Methods are exposed via `#[lua_methods]` attribute macro
117/// on impl blocks, which generates static C wrapper functions returned from `get_field`
118/// as `UdValue::Function(cfunction)`.
119///
120/// # Dispatch priority (when Lua accesses `obj.key`):
121/// 1. `get_field(key)` — field or method access (fields return value, methods return CFunction)
122/// 2. Metatable `__index` — traditional Lua fallback
123///
124/// # Example (manual implementation)
125/// ```ignore
126/// struct Point { x: f64, y: f64 }
127///
128/// impl UserDataTrait for Point {
129/// fn type_name(&self) -> &'static str { "Point" }
130///
131/// fn get_field(&self, key: &str) -> Option<UdValue> {
132/// match key {
133/// "x" => Some(UdValue::Number(self.x)),
134/// "y" => Some(UdValue::Number(self.y)),
135/// _ => None,
136/// }
137/// }
138///
139/// fn set_field(&mut self, key: &str, value: UdValue) -> Option<Result<(), String>> {
140/// match key {
141/// "x" => match value {
142/// UdValue::Number(n) => { self.x = n; Some(Ok(())) }
143/// _ => Some(Err("x must be a number".into()))
144/// }
145/// _ => None,
146/// }
147/// }
148///
149/// fn as_any(&self) -> &dyn Any { self }
150/// fn as_any_mut(&mut self) -> &mut dyn Any { self }
151/// }
152/// ```
153pub trait UserDataTrait: 'static {
154 // ==================== Identity ====================
155
156 /// Returns the type name displayed in error messages and `type()` calls.
157 /// For derive macro: uses the struct name.
158 fn type_name(&self) -> &'static str;
159
160 // ==================== Field Access ====================
161
162 /// Get a field value by name.
163 /// Returns `Some(value)` if the field exists, `None` to fall through to metatable.
164 fn get_field(&self, _key: &str) -> Option<UdValue> {
165 None
166 }
167
168 /// Set a field value by name.
169 /// Returns:
170 /// - `Some(Ok(()))` — field was set successfully
171 /// - `Some(Err(msg))` — field exists but value is invalid (type mismatch, etc.)
172 /// - `None` — field not found, fall through to metatable `__newindex`
173 fn set_field(&mut self, _key: &str, _value: UdValue) -> Option<Result<(), String>> {
174 None
175 }
176
177 // ==================== Metamethods ====================
178 // These are auto-generated by the derive macro when the struct
179 // implements the corresponding Rust trait. Return None = not supported.
180
181 /// `__tostring`: String representation.
182 /// Auto-generated when struct implements `Display`.
183 fn lua_tostring(&self) -> Option<String> {
184 None
185 }
186
187 /// `__eq`: Equality comparison.
188 /// Auto-generated when struct implements `PartialEq`.
189 /// `other` is guaranteed to be a `&dyn UserDataTrait` — downcast via `as_any()`.
190 fn lua_eq(&self, _other: &dyn UserDataTrait) -> Option<bool> {
191 None
192 }
193
194 /// `__lt`: Less-than comparison.
195 /// Auto-generated when struct implements `PartialOrd`.
196 fn lua_lt(&self, _other: &dyn UserDataTrait) -> Option<bool> {
197 None
198 }
199
200 /// `__le`: Less-or-equal comparison.
201 /// Auto-generated when struct implements `PartialOrd`.
202 fn lua_le(&self, _other: &dyn UserDataTrait) -> Option<bool> {
203 None
204 }
205
206 /// `__len`: Length operator (`#obj`).
207 /// Auto-generated when struct has a `.len()` method.
208 fn lua_len(&self) -> Option<UdValue> {
209 None
210 }
211
212 /// `__unm`: Unary minus (`-obj`).
213 /// Auto-generated when struct implements `Neg`.
214 fn lua_unm(&self) -> Option<UdValue> {
215 None
216 }
217
218 /// `__add`: Addition (`obj + other`).
219 /// Auto-generated when struct implements `Add`.
220 fn lua_add(&self, _other: &UdValue) -> Option<UdValue> {
221 None
222 }
223
224 /// `__sub`: Subtraction (`obj - other`).
225 /// Auto-generated when struct implements `Sub`.
226 fn lua_sub(&self, _other: &UdValue) -> Option<UdValue> {
227 None
228 }
229
230 /// `__mul`: Multiplication (`obj * other`).
231 /// Auto-generated when struct implements `Mul`.
232 fn lua_mul(&self, _other: &UdValue) -> Option<UdValue> {
233 None
234 }
235
236 /// `__div`: Division (`obj / other`).
237 /// Auto-generated when struct implements `Div`.
238 fn lua_div(&self, _other: &UdValue) -> Option<UdValue> {
239 None
240 }
241
242 /// `__mod`: Modulo (`obj % other`).
243 /// Auto-generated when struct implements `Rem`.
244 fn lua_mod(&self, _other: &UdValue) -> Option<UdValue> {
245 None
246 }
247
248 /// `__concat`: Concatenation (`obj .. other`).
249 fn lua_concat(&self, _other: &UdValue) -> Option<UdValue> {
250 None
251 }
252
253 /// `__gc`: Called when the userdata is garbage collected.
254 /// Use this for cleanup (closing files, releasing resources, etc.).
255 fn lua_gc(&mut self) {}
256
257 /// `__close`: Called when a to-be-closed variable goes out of scope.
258 fn lua_close(&mut self) {}
259
260 /// `__call`: Makes the userdata callable like a function.
261 ///
262 /// Return `Some(cfunction)` to make `obj(args...)` work from Lua.
263 /// The CFunction receives `self` (the userdata) as arg 1, followed by
264 /// the caller's arguments.
265 ///
266 /// This is checked before the metatable `__call` fallback.
267 ///
268 /// # Example
269 /// ```ignore
270 /// fn lua_call(&self) -> Option<CFunction> {
271 /// fn call_impl(l: &mut LuaState) -> LuaResult<usize> {
272 /// let ud = l.get_arg(1).unwrap();
273 /// let x = l.get_arg(2).and_then(|v| v.as_integer()).unwrap_or(0);
274 /// l.push_value(LuaValue::integer(x * 2))?;
275 /// Ok(1)
276 /// }
277 /// Some(call_impl)
278 /// }
279 /// ```
280 fn lua_call(&self) -> Option<crate::lua_vm::CFunction> {
281 None
282 }
283
284 // ==================== Iteration ====================
285
286 /// Stateless iterator: given the current control variable, return the next
287 /// `(control, value)` pair, or `None` to stop.
288 ///
289 /// This follows Lua's generic-for protocol:
290 /// ```lua
291 /// for k, v in pairs(ud) do ... end
292 /// ```
293 ///
294 /// The control variable starts as `UdValue::Nil`. Implementors decide
295 /// what it represents (e.g., an integer index for sequences).
296 ///
297 /// # Example (Vec-like)
298 /// ```ignore
299 /// fn lua_next(&self, control: &UdValue) -> Option<(UdValue, UdValue)> {
300 /// let idx = match control {
301 /// UdValue::Nil => 0,
302 /// UdValue::Integer(i) => *i as usize,
303 /// _ => return None,
304 /// };
305 /// self.items.get(idx).map(|v| (
306 /// UdValue::Integer((idx + 1) as i64),
307 /// UdValue::Integer(*v as i64),
308 /// ))
309 /// }
310 /// ```
311 fn lua_next(&self, _control: &UdValue) -> Option<(UdValue, UdValue)> {
312 None
313 }
314
315 // ==================== Reflection ====================
316
317 /// List available field names (for debugging, iteration, auto-completion).
318 fn field_names(&self) -> &'static [&'static str] {
319 &[]
320 }
321
322 // ==================== Downcasting ====================
323 // Required for backward compatibility and type-specific access.
324 // The derive macro auto-generates these.
325
326 /// Downcast to `&dyn Any` for type-specific access.
327 fn as_any(&self) -> &dyn Any;
328
329 /// Downcast to `&mut dyn Any` for mutable type-specific access.
330 fn as_any_mut(&mut self) -> &mut dyn Any;
331}
332
333// ==================== UdValue ↔ Rust type conversions ====================
334
335impl From<bool> for UdValue {
336 fn from(b: bool) -> Self {
337 UdValue::Boolean(b)
338 }
339}
340
341impl From<i64> for UdValue {
342 fn from(i: i64) -> Self {
343 UdValue::Integer(i)
344 }
345}
346
347impl From<i32> for UdValue {
348 fn from(i: i32) -> Self {
349 UdValue::Integer(i as i64)
350 }
351}
352
353impl From<f64> for UdValue {
354 fn from(n: f64) -> Self {
355 UdValue::Number(n)
356 }
357}
358
359impl From<f32> for UdValue {
360 fn from(n: f32) -> Self {
361 UdValue::Number(n as f64)
362 }
363}
364
365impl From<String> for UdValue {
366 fn from(s: String) -> Self {
367 UdValue::Str(s)
368 }
369}
370
371impl From<&str> for UdValue {
372 fn from(s: &str) -> Self {
373 UdValue::Str(s.to_owned())
374 }
375}
376
377impl<T: Into<UdValue>> From<Option<T>> for UdValue {
378 fn from(opt: Option<T>) -> Self {
379 match opt {
380 Some(v) => v.into(),
381 None => UdValue::Nil,
382 }
383 }
384}
385
386// ==================== UdValue → Rust type extraction ====================
387
388impl UdValue {
389 /// Extract as bool. Follows Lua truthiness: nil and false are false, everything else is true.
390 pub fn to_bool(&self) -> bool {
391 match self {
392 UdValue::Nil => false,
393 UdValue::Boolean(b) => *b,
394 _ => true,
395 }
396 }
397
398 /// Extract as i64 (with optional float→int coercion).
399 pub fn to_integer(&self) -> Option<i64> {
400 match self {
401 UdValue::Integer(i) => Some(*i),
402 UdValue::Number(n) => {
403 let i = *n as i64;
404 if (i as f64) == *n { Some(i) } else { None }
405 }
406 _ => None,
407 }
408 }
409
410 /// Extract as f64 (with optional int→float coercion).
411 pub fn to_number(&self) -> Option<f64> {
412 match self {
413 UdValue::Number(n) => Some(*n),
414 UdValue::Integer(i) => Some(*i as f64),
415 _ => None,
416 }
417 }
418
419 /// Extract as string reference.
420 pub fn to_str(&self) -> Option<&str> {
421 match self {
422 UdValue::Str(s) => Some(s.as_str()),
423 _ => None,
424 }
425 }
426}
427
428impl fmt::Debug for UdValue {
429 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
430 match self {
431 UdValue::Nil => write!(f, "Nil"),
432 UdValue::Boolean(b) => write!(f, "Boolean({})", b),
433 UdValue::Integer(i) => write!(f, "Integer({})", i),
434 UdValue::Number(n) => write!(f, "Number({})", n),
435 UdValue::Str(s) => write!(f, "Str({:?})", s),
436 UdValue::Function(_) => write!(f, "Function(<cfunction>)"),
437 UdValue::UserdataRef(_) => write!(f, "UserdataRef(<ptr>)"),
438 UdValue::UserdataOwned(ud) => write!(f, "UserdataOwned({})", ud.type_name()),
439 }
440 }
441}
442
443impl fmt::Display for UdValue {
444 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
445 match self {
446 UdValue::Nil => write!(f, "nil"),
447 UdValue::Boolean(b) => write!(f, "{}", b),
448 UdValue::Integer(i) => write!(f, "{}", i),
449 UdValue::Number(n) => write!(f, "{}", n),
450 UdValue::Str(s) => write!(f, "{}", s),
451 UdValue::Function(_) => write!(f, "function"),
452 UdValue::UserdataRef(_) => write!(f, "userdata"),
453 UdValue::UserdataOwned(ud) => write!(f, "{}", ud.type_name()),
454 }
455 }
456}
457
458// ==================== UdValue ↔ LuaValue conversion ====================
459
460/// Convert a `UdValue` to a `LuaValue`.
461///
462/// Most variants are zero-cost. `UdValue::Str` requires GC allocation via `LuaState`.
463/// This is the bridge between trait-based dispatch (which returns `UdValue`) and the
464/// VM's internal representation (`LuaValue`).
465pub fn udvalue_to_lua_value(
466 lua_state: &mut crate::lua_vm::LuaState,
467 udv: UdValue,
468) -> crate::lua_vm::LuaResult<crate::lua_value::LuaValue> {
469 use crate::lua_value::LuaValue;
470 match udv {
471 UdValue::Nil => Ok(LuaValue::nil()),
472 UdValue::Boolean(b) => Ok(LuaValue::boolean(b)),
473 UdValue::Integer(i) => Ok(LuaValue::integer(i)),
474 UdValue::Number(n) => Ok(LuaValue::float(n)),
475 UdValue::Str(s) => lua_state.create_string(&s),
476 UdValue::Function(f) => Ok(LuaValue::cfunction(f)),
477 UdValue::UserdataRef(_) => Ok(LuaValue::nil()), // should not be returned
478 UdValue::UserdataOwned(ud) => {
479 use crate::lua_value::LuaUserdata;
480 let userdata = LuaUserdata::from_boxed(ud);
481 lua_state.create_userdata(userdata)
482 }
483 }
484}
485
486/// Convert a `LuaValue` to a `UdValue`.
487///
488/// Lossless for nil, bool, int, float, string. Other types (table, function, etc.)
489/// become `UdValue::Nil` since they can't be represented in the trait world.
490pub fn lua_value_to_udvalue(value: &crate::lua_value::LuaValue) -> UdValue {
491 if value.is_nil() {
492 UdValue::Nil
493 } else if let Some(b) = value.as_boolean() {
494 UdValue::Boolean(b)
495 } else if let Some(i) = value.as_integer() {
496 UdValue::Integer(i)
497 } else if let Some(n) = value.as_float() {
498 UdValue::Number(n)
499 } else if let Some(s) = value.as_str() {
500 UdValue::Str(s.to_owned())
501 } else if let Some(ud) = value.as_userdata_mut() {
502 // Carry userdata reference so arithmetic trait methods can downcast
503 UdValue::UserdataRef(ud.get_trait().as_any() as *const dyn Any)
504 } else {
505 UdValue::Nil
506 }
507}
508
509// ==================== Convenience macro for simple types ====================
510
511/// Implement `UserDataTrait` for types that only need type name and downcast support.
512/// These types use metatables for their Lua-visible API (e.g., IO file handles).
513///
514/// ```ignore
515/// impl_simple_userdata!(LuaFile, "FILE*");
516/// ```
517#[macro_export]
518macro_rules! impl_simple_userdata {
519 ($ty:ty, $name:expr) => {
520 impl $crate::lua_value::userdata_trait::UserDataTrait for $ty {
521 fn type_name(&self) -> &'static str {
522 $name
523 }
524
525 fn as_any(&self) -> &dyn std::any::Any {
526 self
527 }
528
529 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
530 self
531 }
532 }
533 };
534}
535
536// ==================== Method Provider ====================
537
538/// Blanket trait providing a default no-op method lookup.
539///
540/// When `#[lua_methods]` attribute macro is used on an impl block,
541/// it generates an inherent `__lua_lookup_method` function on the type
542/// that shadows this trait's default. The derive macro's `get_field`
543/// calls `Self::__lua_lookup_method(key)` which resolves to the
544/// inherent method (if defined) or falls back to this default.
545pub trait LuaMethodProvider {
546 fn __lua_lookup_method(_key: &str) -> Option<CFunction> {
547 None
548 }
549}
550
551impl<T> LuaMethodProvider for T {}
552
553// ==================== Type Registration ====================
554
555/// Blanket trait providing a default empty static methods list.
556///
557/// When `#[lua_methods]` is used on an impl block that contains associated
558/// functions (no `self`), it generates an inherent `__lua_static_methods()`
559/// function on the type that shadows this trait's default.
560///
561/// Used by `LuaState::register_type::<T>(name)` to populate the class table.
562pub trait LuaStaticMethodProvider {
563 fn __lua_static_methods() -> &'static [(&'static str, CFunction)] {
564 &[]
565 }
566}
567
568impl<T> LuaStaticMethodProvider for T {}
569
570/// Trait for types that can be registered with Lua via `register_type_of::<T>`.
571///
572/// This trait is explicitly implemented by `#[lua_methods]` (no blanket impl),
573/// so `T::lua_static_methods()` dispatches to the actual generated methods.
574///
575/// Unlike `LuaStaticMethodProvider` (which has a blanket impl that always
576/// returns `&[]`), this trait guarantees that the type has real method info.
577pub trait LuaRegistrable {
578 /// Return all static (associated) methods for type registration.
579 fn lua_static_methods() -> &'static [(&'static str, CFunction)];
580}
581
582// ==================== Enum Export ====================
583
584/// Trait for Rust enums that can be exported to Lua as a table of constants.
585///
586/// Automatically implemented by `#[derive(LuaUserData)]` on C-like enums
587/// (enums with no data fields). Each variant becomes a key-value pair in
588/// a Lua table.
589///
590/// # Example
591///
592/// ```ignore
593/// #[derive(LuaUserData)]
594/// enum Color {
595/// Red, // 0
596/// Green, // 1
597/// Blue, // 2
598/// }
599///
600/// // With explicit discriminants:
601/// #[derive(LuaUserData)]
602/// enum HttpStatus {
603/// Ok = 200,
604/// NotFound = 404,
605/// ServerError = 500,
606/// }
607///
608/// // Register in Lua:
609/// vm.register_enum::<Color>("Color")?;
610/// // Lua: Color.Red == 0, Color.Green == 1, Color.Blue == 2
611/// ```
612pub trait LuaEnum {
613 /// Return variant name-value pairs for Lua table construction.
614 fn variants() -> &'static [(&'static str, i64)];
615
616 /// Return the enum's type name.
617 fn enum_name() -> &'static str;
618}
619
620// ==================== OpaqueUserData ====================
621
622/// Wraps any `T: 'static` as an opaque Lua userdata.
623///
624/// No fields, methods, or metamethods are exposed — the value is a "black box"
625/// in Lua. From Rust you can recover the original type via `downcast_ref::<T>()`.
626///
627/// Use [`LuaVM::push_any`] to create one conveniently.
628///
629/// # Example
630///
631/// ```ignore
632/// // Third-party type you don't control
633/// let client = reqwest::Client::new();
634/// let ud = vm.push_any(client)?;
635/// vm.set_global("http_client", ud)?;
636///
637/// // Later, in a Rust callback:
638/// let client = ud_value.downcast_ref::<reqwest::Client>().unwrap();
639/// ```
640pub struct OpaqueUserData<T: 'static> {
641 value: T,
642}
643
644impl<T: 'static> OpaqueUserData<T> {
645 /// Wrap a value.
646 pub fn new(value: T) -> Self {
647 OpaqueUserData { value }
648 }
649
650 /// Get a reference to the inner value.
651 pub fn inner(&self) -> &T {
652 &self.value
653 }
654
655 /// Get a mutable reference to the inner value.
656 pub fn inner_mut(&mut self) -> &mut T {
657 &mut self.value
658 }
659}
660
661impl<T: 'static> UserDataTrait for OpaqueUserData<T> {
662 fn type_name(&self) -> &'static str {
663 std::any::type_name::<T>()
664 }
665
666 fn as_any(&self) -> &dyn Any {
667 // Downcast to T (not OpaqueUserData<T>) for ergonomic access
668 &self.value
669 }
670
671 fn as_any_mut(&mut self) -> &mut dyn Any {
672 &mut self.value
673 }
674}
675
676// ==================== RefUserData — zero-cost borrowed reference ====================
677
678/// A userdata wrapper that holds a raw pointer to an external `T: UserDataTrait`.
679///
680/// This enables passing Rust objects to Lua **by reference** without transferring
681/// ownership. The object lives on the Rust side; Lua sees a full userdata with
682/// field access, method calls, and metamethods — all forwarded through the pointer.
683///
684/// # Performance
685/// Zero overhead compared to owned userdata — no `Rc`, no `RefCell`, no
686/// atomic operations. The pointer is dereferenced directly on every access.
687///
688/// # Safety
689/// The caller **must** guarantee that the pointee outlives every Lua access.
690/// Accessing the userdata after the Rust object is dropped is undefined behavior.
691/// This is deliberately an `unsafe` API — the same contract as C Lua's light
692/// userdata, but with full trait-based dispatch.
693///
694/// # Example
695/// ```ignore
696/// let mut player = Player::new("Alice", 100);
697///
698/// // Lend to Lua (no ownership transfer)
699/// let ud_val = state.create_userdata_ref(&mut player)?;
700/// state.set_global("player", ud_val)?;
701///
702/// // Lua can read/write fields, call methods — all forwarded to `player`
703/// state.execute_string(r#"
704/// print(player.name) -- "Alice"
705/// player:take_damage(10)
706/// print(player.hp) -- 90
707/// "#)?;
708///
709/// // Back in Rust, `player` reflects the mutations
710/// assert_eq!(player.hp, 90);
711/// ```
712pub struct RefUserData<T: UserDataTrait> {
713 ptr: *mut T,
714}
715
716impl<T: UserDataTrait> RefUserData<T> {
717 /// Create a `RefUserData` from a mutable reference.
718 ///
719 /// # Safety
720 /// The referenced object must outlive all Lua accesses to this userdata.
721 #[inline]
722 pub unsafe fn new(reference: &mut T) -> Self {
723 RefUserData {
724 ptr: reference as *mut T,
725 }
726 }
727
728 /// Create from a raw pointer.
729 ///
730 /// # Safety
731 /// The pointer must be valid and properly aligned for the entire duration
732 /// that Lua can access this userdata.
733 #[inline]
734 pub unsafe fn from_raw(ptr: *mut T) -> Self {
735 RefUserData { ptr }
736 }
737
738 #[inline]
739 fn inner(&self) -> &T {
740 unsafe { &*self.ptr }
741 }
742
743 #[inline]
744 fn inner_mut(&mut self) -> &mut T {
745 unsafe { &mut *self.ptr }
746 }
747}
748
749/// Forward every `UserDataTrait` method to the pointee.
750///
751/// This is intentionally not `'static` on `T` alone — the `'static` bound
752/// comes from `UserDataTrait: 'static`. The raw pointer erases the lifetime;
753/// correctness relies on the caller's safety guarantee.
754impl<T: UserDataTrait> UserDataTrait for RefUserData<T> {
755 #[inline]
756 fn type_name(&self) -> &'static str {
757 self.inner().type_name()
758 }
759
760 #[inline]
761 fn get_field(&self, key: &str) -> Option<UdValue> {
762 self.inner().get_field(key)
763 }
764
765 #[inline]
766 fn set_field(&mut self, key: &str, value: UdValue) -> Option<Result<(), String>> {
767 self.inner_mut().set_field(key, value)
768 }
769
770 fn lua_tostring(&self) -> Option<String> {
771 self.inner().lua_tostring()
772 }
773
774 fn lua_eq(&self, other: &dyn UserDataTrait) -> Option<bool> {
775 self.inner().lua_eq(other)
776 }
777
778 fn lua_lt(&self, other: &dyn UserDataTrait) -> Option<bool> {
779 self.inner().lua_lt(other)
780 }
781
782 fn lua_le(&self, other: &dyn UserDataTrait) -> Option<bool> {
783 self.inner().lua_le(other)
784 }
785
786 fn lua_len(&self) -> Option<UdValue> {
787 self.inner().lua_len()
788 }
789
790 fn lua_unm(&self) -> Option<UdValue> {
791 self.inner().lua_unm()
792 }
793
794 fn lua_add(&self, other: &UdValue) -> Option<UdValue> {
795 self.inner().lua_add(other)
796 }
797
798 fn lua_sub(&self, other: &UdValue) -> Option<UdValue> {
799 self.inner().lua_sub(other)
800 }
801
802 fn lua_mul(&self, other: &UdValue) -> Option<UdValue> {
803 self.inner().lua_mul(other)
804 }
805
806 fn lua_div(&self, other: &UdValue) -> Option<UdValue> {
807 self.inner().lua_div(other)
808 }
809
810 fn lua_mod(&self, other: &UdValue) -> Option<UdValue> {
811 self.inner().lua_mod(other)
812 }
813
814 fn lua_concat(&self, other: &UdValue) -> Option<UdValue> {
815 self.inner().lua_concat(other)
816 }
817
818 fn lua_gc(&mut self) {
819 // Intentionally a no-op: we don't own the data, so GC must not drop it.
820 }
821
822 fn lua_close(&mut self) {
823 // Same: no-op for borrowed references.
824 }
825
826 fn field_names(&self) -> &'static [&'static str] {
827 self.inner().field_names()
828 }
829
830 fn as_any(&self) -> &dyn Any {
831 self.inner().as_any()
832 }
833
834 fn as_any_mut(&mut self) -> &mut dyn Any {
835 self.inner_mut().as_any_mut()
836 }
837}