Skip to main content

mlua/
table.rs

1//! Lua table handling.
2//!
3//! Tables are Lua's primary data structure, used for arrays, dictionaries, objects, modules,
4//! and more. This module provides types for creating and manipulating Lua tables from Rust.
5//!
6//! # Basic Operations
7//!
8//! Tables support key-value access similar to Rust's `HashMap`:
9//!
10//! ```
11//! # use mlua::{Lua, Result};
12//! # fn main() -> Result<()> {
13//! let lua = Lua::new();
14//! let table = lua.create_table()?;
15//!
16//! // Set and get values
17//! table.set("key", "value")?;
18//! let value: String = table.get("key")?;
19//! assert_eq!(value, "value");
20//!
21//! // Keys and values can be any Lua-compatible type
22//! table.set(1, "first")?;
23//! table.set("nested", lua.create_table()?)?;
24//! # Ok(())
25//! # }
26//! ```
27//!
28//! # Array Operations
29//!
30//! Tables can be used as arrays with 1-based indexing:
31//!
32//! ```
33//! # use mlua::{Lua, Result};
34//! # fn main() -> Result<()> {
35//! let lua = Lua::new();
36//! let array = lua.create_table()?;
37//!
38//! // Push values to the end (like Vec::push)
39//! array.push("first")?;
40//! array.push("second")?;
41//! array.push("third")?;
42//!
43//! // Pop from the end
44//! let last: String = array.pop()?;
45//! assert_eq!(last, "third");
46//!
47//! // Get length
48//! assert_eq!(array.raw_len(), 2);
49//! # Ok(())
50//! # }
51//! ```
52//!
53//! # Iteration
54//!
55//! Iterate over all key-value pairs with [`Table::pairs`]:
56//!
57//! ```
58//! # use mlua::{Lua, Result, Value};
59//! # fn main() -> Result<()> {
60//! let lua = Lua::new();
61//! let table = lua.create_table()?;
62//! table.set("a", 1)?;
63//! table.set("b", 2)?;
64//!
65//! for pair in table.pairs::<String, i32>() {
66//!     let (key, value) = pair?;
67//!     println!("{key} = {value}");
68//! }
69//! # Ok(())
70//! # }
71//! ```
72//!
73//! For array portions, use [`Table::sequence_values`]:
74//!
75//! ```
76//! # use mlua::{Lua, Result};
77//! # fn main() -> Result<()> {
78//! let lua = Lua::new();
79//! let array = lua.create_sequence_from(["a", "b", "c"])?;
80//!
81//! for value in array.sequence_values::<String>() {
82//!     println!("{}", value?);
83//! }
84//! # Ok(())
85//! # }
86//! ```
87//!
88//! # Raw vs Normal Access
89//!
90//! Methods prefixed with `raw_` (like [`Table::raw_get`], [`Table::raw_set`]) bypass
91//! metamethods, directly accessing the table's contents. Normal methods may trigger
92//! `__index`, `__newindex`, and other metamethods:
93//!
94//! ```
95//! # use mlua::{Lua, Result};
96//! # fn main() -> Result<()> {
97//! let lua = Lua::new();
98//!
99//! // raw_set bypasses __newindex metamethod
100//! let t = lua.create_table()?;
101//! t.raw_set("key", "value")?;
102//!
103//! // raw_get bypasses __index metamethod
104//! let v: String = t.raw_get("key")?;
105//! # Ok(())
106//! # }
107//! ```
108//!
109//! # Metatables
110//!
111//! Tables can have metatables that customize their behavior:
112//!
113//! ```
114//! # use mlua::{Lua, Result};
115//! # fn main() -> Result<()> {
116//! let lua = Lua::new();
117//!
118//! let table = lua.create_table()?;
119//! let metatable = lua.create_table()?;
120//!
121//! // Set a default value via __index
122//! metatable.set("__index", lua.create_function(|_, _: ()| Ok("default"))?)?;
123//! table.set_metatable(Some(metatable))?;
124//!
125//! // Accessing missing keys returns "default"
126//! let value: String = table.get("missing")?;
127//! assert_eq!(value, "default");
128//! # Ok(())
129//! # }
130//! ```
131//!
132//! # Global Table
133//!
134//! The Lua global environment is itself a table, accessible via [`Lua::globals`]:
135//!
136//! ```
137//! # use mlua::{Lua, Result};
138//! # fn main() -> Result<()> {
139//! let lua = Lua::new();
140//! let globals = lua.globals();
141//!
142//! // Set a global variable
143//! globals.set("my_var", 42)?;
144//!
145//! // Now accessible from Lua code
146//! let result: i32 = lua.load("my_var + 8").eval()?;
147//! assert_eq!(result, 50);
148//! # Ok(())
149//! # }
150//! ```
151//!
152//! [`Lua::globals`]: crate::Lua::globals
153
154use std::collections::HashSet;
155use std::fmt;
156use std::marker::PhantomData;
157use std::os::raw::c_void;
158
159use crate::error::{Error, Result};
160use crate::function::Function;
161use crate::state::{LuaGuard, RawLua, WeakLua};
162use crate::traits::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, ObjectLike};
163use crate::types::{Integer, ValueRef};
164use crate::util::{StackGuard, assert_stack, check_stack, get_metatable_ptr};
165use crate::value::{Nil, Value};
166
167#[cfg(feature = "async")]
168use crate::function::AsyncCallFuture;
169
170#[cfg(feature = "serde")]
171use {
172    rustc_hash::FxHashSet,
173    serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
174    std::{cell::RefCell, rc::Rc, result::Result as StdResult},
175};
176
177/// Handle to an internal Lua table.
178#[derive(Clone, PartialEq)]
179pub struct Table(pub(crate) ValueRef);
180
181impl Table {
182    /// Sets a key-value pair in the table.
183    ///
184    /// If the value is `nil`, this will effectively remove the pair.
185    ///
186    /// This might invoke the `__newindex` metamethod. Use the [`raw_set`] method if that is not
187    /// desired.
188    ///
189    /// # Examples
190    ///
191    /// Export a value as a global to make it usable from Lua:
192    ///
193    /// ```
194    /// # use mlua::{Lua, Result};
195    /// # fn main() -> Result<()> {
196    /// # let lua = Lua::new();
197    /// let globals = lua.globals();
198    ///
199    /// globals.set("assertions", cfg!(debug_assertions))?;
200    ///
201    /// lua.load(r#"
202    ///     if assertions == true then
203    ///         -- ...
204    ///     elseif assertions == false then
205    ///         -- ...
206    ///     else
207    ///         error("assertions neither on nor off?")
208    ///     end
209    /// "#).exec()?;
210    /// # Ok(())
211    /// # }
212    /// ```
213    ///
214    /// [`raw_set`]: Table::raw_set
215    pub fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
216        // Fast track (skip protected call)
217        if !self.has_metatable() {
218            return self.raw_set(key, value);
219        }
220
221        self.set_protected(key, value)
222    }
223
224    pub(crate) fn set_protected(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
225        let lua = self.0.lua.lock();
226        let state = lua.state();
227        unsafe {
228            let _sg = StackGuard::new(state);
229            check_stack(state, 5)?;
230
231            lua.push_ref(&self.0);
232            key.push_into_stack(&lua)?;
233            value.push_into_stack(&lua)?;
234            protect_lua!(state, 3, 0, fn(state) ffi::lua_settable(state, -3))
235        }
236    }
237
238    /// Gets the value associated to `key` from the table.
239    ///
240    /// If no value is associated to `key`, returns the `nil` value.
241    ///
242    /// This might invoke the `__index` metamethod. Use the [`raw_get`] method if that is not
243    /// desired.
244    ///
245    /// # Examples
246    ///
247    /// Query the version of the Lua interpreter:
248    ///
249    /// ```
250    /// # use mlua::{Lua, Result};
251    /// # fn main() -> Result<()> {
252    /// # let lua = Lua::new();
253    /// let globals = lua.globals();
254    ///
255    /// let version: String = globals.get("_VERSION")?;
256    /// println!("Lua version: {}", version);
257    /// # Ok(())
258    /// # }
259    /// ```
260    ///
261    /// [`raw_get`]: Table::raw_get
262    pub fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
263        // Fast track (skip protected call)
264        if !self.has_metatable() {
265            return self.raw_get(key);
266        }
267
268        self.get_protected(key)
269    }
270
271    pub(crate) fn get_protected<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
272        let lua = self.0.lua.lock();
273        let state = lua.state();
274        unsafe {
275            let _sg = StackGuard::new(state);
276            check_stack(state, 4)?;
277
278            lua.push_ref(&self.0);
279            key.push_into_stack(&lua)?;
280            protect_lua!(state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
281
282            V::from_stack(-1, &lua)
283        }
284    }
285
286    /// Checks whether the table contains a non-nil value for `key`.
287    ///
288    /// This might invoke the `__index` metamethod.
289    pub fn contains_key(&self, key: impl IntoLua) -> Result<bool> {
290        Ok(self.get::<Value>(key)? != Value::Nil)
291    }
292
293    /// Appends a value to the back of the table.
294    ///
295    /// This might invoke the `__len` and `__newindex` metamethods.
296    pub fn push(&self, value: impl IntoLua) -> Result<()> {
297        // Fast track (skip protected call)
298        if !self.has_metatable() {
299            return self.raw_push(value);
300        }
301
302        let lua = self.0.lua.lock();
303        let state = lua.state();
304        unsafe {
305            let _sg = StackGuard::new(state);
306            check_stack(state, 4)?;
307
308            lua.push_ref(&self.0);
309            value.push_into_stack(&lua)?;
310            protect_lua!(state, 2, 0, fn(state) {
311                let len = ffi::luaL_len(state, -2) as Integer;
312                ffi::lua_seti(state, -2, len + 1);
313            })?
314        }
315        Ok(())
316    }
317
318    /// Removes the last element from the table and returns it.
319    ///
320    /// This might invoke the `__len` and `__newindex` metamethods.
321    pub fn pop<V: FromLua>(&self) -> Result<V> {
322        // Fast track (skip protected call)
323        if !self.has_metatable() {
324            return self.raw_pop();
325        }
326
327        let lua = self.0.lua.lock();
328        let state = lua.state();
329        unsafe {
330            let _sg = StackGuard::new(state);
331            check_stack(state, 4)?;
332
333            lua.push_ref(&self.0);
334            protect_lua!(state, 1, 1, fn(state) {
335                let len = ffi::luaL_len(state, -1) as Integer;
336                ffi::lua_geti(state, -1, len);
337                ffi::lua_pushnil(state);
338                ffi::lua_seti(state, -3, len);
339            })?;
340            V::from_stack(-1, &lua)
341        }
342    }
343
344    /// Removes a key from the table.
345    ///
346    /// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
347    /// and erases element `table[key]`. The complexity is `O(n)` in the worst case,
348    /// where `n` is the table length.
349    ///
350    /// For other key types this is equivalent to setting `table[key] = nil`.
351    ///
352    /// This might invoke the `__len`, `__index` and `__newindex` metamethods.
353    /// Use the [`raw_remove`] method if that is not desired.
354    ///
355    /// [`raw_remove`]: Table::raw_remove
356    pub fn remove(&self, key: impl IntoLua) -> Result<()> {
357        // Fast track (skip protected call)
358        if !self.has_metatable() {
359            return self.raw_remove(key);
360        }
361
362        let lua = self.0.lua.lock();
363        let key = key.into_lua(lua.lua())?;
364        match key {
365            Value::Integer(idx) => {
366                let size = self.len()?;
367                if idx < 1 || idx > size {
368                    return Err(Error::runtime("index out of bounds"));
369                }
370
371                let state = lua.state();
372                unsafe {
373                    let _sg = StackGuard::new(state);
374                    check_stack(state, 4)?;
375
376                    lua.push_ref(&self.0);
377                    protect_lua!(state, 1, 0, |state| {
378                        for i in idx..size {
379                            // table[i] = table[i+1]
380                            ffi::lua_geti(state, -1, i + 1);
381                            ffi::lua_seti(state, -2, i);
382                        }
383                        ffi::lua_pushnil(state);
384                        ffi::lua_seti(state, -2, size);
385                    })
386                }
387            }
388            _ => self.set(key, Nil),
389        }
390    }
391
392    /// Compares two tables for equality.
393    ///
394    /// Tables are compared by reference first.
395    /// If they are not primitively equals, then mlua will try to invoke the `__eq` metamethod.
396    /// mlua will check `self` first for the metamethod, then `other` if not found.
397    ///
398    /// # Examples
399    ///
400    /// Compare two tables using `__eq` metamethod:
401    ///
402    /// ```
403    /// # use mlua::{Lua, Result, Table};
404    /// # fn main() -> Result<()> {
405    /// # let lua = Lua::new();
406    /// let table1 = lua.create_table()?;
407    /// table1.set(1, "value")?;
408    ///
409    /// let table2 = lua.create_table()?;
410    /// table2.set(2, "value")?;
411    ///
412    /// let always_equals_mt = lua.create_table()?;
413    /// always_equals_mt.set("__eq", lua.create_function(|_, (_t1, _t2): (Table, Table)| Ok(true))?)?;
414    /// table2.set_metatable(Some(always_equals_mt))?;
415    ///
416    /// assert!(table1.equals(&table1.clone())?);
417    /// assert!(table1.equals(&table2)?);
418    /// # Ok(())
419    /// # }
420    /// ```
421    pub fn equals(&self, other: &Self) -> Result<bool> {
422        if self == other {
423            return Ok(true);
424        }
425
426        // Compare using `__eq` metamethod if exists
427        // First, check the self for the metamethod.
428        // If self does not define it, then check the other table.
429        if let Some(mt) = self.metatable()
430            && let Some(eq_func) = mt.get::<Option<Function>>("__eq")?
431        {
432            return eq_func.call((self, other));
433        }
434        if let Some(mt) = other.metatable()
435            && let Some(eq_func) = mt.get::<Option<Function>>("__eq")?
436        {
437            return eq_func.call((self, other));
438        }
439
440        Ok(false)
441    }
442
443    /// Sets a key-value pair without invoking metamethods.
444    pub fn raw_set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
445        let lua = self.0.lua.lock();
446        let state = lua.state();
447        unsafe {
448            #[cfg(feature = "luau")]
449            self.check_readonly_write(&lua)?;
450
451            let _sg = StackGuard::new(state);
452            check_stack(state, 5)?;
453
454            lua.push_ref(&self.0);
455            key.push_into_stack(&lua)?;
456            value.push_into_stack(&lua)?;
457
458            if lua.unlikely_memory_error() {
459                ffi::lua_rawset(state, -3);
460                ffi::lua_pop(state, 1);
461                Ok(())
462            } else {
463                protect_lua!(state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
464            }
465        }
466    }
467
468    /// Gets the value associated to `key` without invoking metamethods.
469    pub fn raw_get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
470        let lua = self.0.lua.lock();
471        let state = lua.state();
472        unsafe {
473            let _sg = StackGuard::new(state);
474            check_stack(state, 3)?;
475
476            lua.push_ref(&self.0);
477            key.push_into_stack(&lua)?;
478            ffi::lua_rawget(state, -2);
479
480            V::from_stack(-1, &lua)
481        }
482    }
483
484    /// Inserts element value at position `idx` to the table, shifting up the elements from
485    /// `table[idx]`.
486    ///
487    /// The worst case complexity is O(n), where n is the table length.
488    pub fn raw_insert(&self, idx: Integer, value: impl IntoLua) -> Result<()> {
489        let size = self.raw_len() as Integer;
490        if idx < 1 || idx > size + 1 {
491            return Err(Error::runtime("index out of bounds"));
492        }
493
494        let lua = self.0.lua.lock();
495        let state = lua.state();
496        unsafe {
497            let _sg = StackGuard::new(state);
498            check_stack(state, 5)?;
499
500            lua.push_ref(&self.0);
501            value.push_into_stack(&lua)?;
502            protect_lua!(state, 2, 0, |state| {
503                for i in (idx..=size).rev() {
504                    // table[i+1] = table[i]
505                    ffi::lua_rawgeti(state, -2, i);
506                    ffi::lua_rawseti(state, -3, i + 1);
507                }
508                ffi::lua_rawseti(state, -2, idx)
509            })
510        }
511    }
512
513    /// Appends a value to the back of the table without invoking metamethods.
514    pub fn raw_push(&self, value: impl IntoLua) -> Result<()> {
515        let lua = self.0.lua.lock();
516        let state = lua.state();
517        unsafe {
518            #[cfg(feature = "luau")]
519            self.check_readonly_write(&lua)?;
520
521            let _sg = StackGuard::new(state);
522            check_stack(state, 4)?;
523
524            lua.push_ref(&self.0);
525            value.push_into_stack(&lua)?;
526
527            unsafe fn callback(state: *mut ffi::lua_State) {
528                let len = ffi::lua_rawlen(state, -2) as Integer;
529                ffi::lua_rawseti(state, -2, len + 1);
530            }
531
532            if lua.unlikely_memory_error() {
533                callback(state);
534            } else {
535                protect_lua!(state, 2, 0, fn(state) callback(state))?;
536            }
537        }
538        Ok(())
539    }
540
541    /// Removes the last element from the table and returns it, without invoking metamethods.
542    pub fn raw_pop<V: FromLua>(&self) -> Result<V> {
543        let lua = self.0.lua.lock();
544        let state = lua.state();
545        unsafe {
546            #[cfg(feature = "luau")]
547            self.check_readonly_write(&lua)?;
548
549            let _sg = StackGuard::new(state);
550            check_stack(state, 3)?;
551
552            lua.push_ref(&self.0);
553            let len = ffi::lua_rawlen(state, -1) as Integer;
554            ffi::lua_rawgeti(state, -1, len);
555            // Set slot to nil (it must be safe to do)
556            ffi::lua_pushnil(state);
557            ffi::lua_rawseti(state, -3, len);
558
559            V::from_stack(-1, &lua)
560        }
561    }
562
563    /// Removes a key from the table.
564    ///
565    /// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
566    /// and erases element `table[key]`. The complexity is `O(n)` in the worst case,
567    /// where `n` is the table length.
568    ///
569    /// For other key types this is equivalent to setting `table[key] = nil`.
570    pub fn raw_remove(&self, key: impl IntoLua) -> Result<()> {
571        let lua = self.0.lua.lock();
572        let state = lua.state();
573        let key = key.into_lua(lua.lua())?;
574        match key {
575            Value::Integer(idx) => {
576                let size = self.raw_len() as Integer;
577                if idx < 1 || idx > size {
578                    return Err(Error::runtime("index out of bounds"));
579                }
580                unsafe {
581                    let _sg = StackGuard::new(state);
582                    check_stack(state, 4)?;
583
584                    lua.push_ref(&self.0);
585                    protect_lua!(state, 1, 0, |state| {
586                        for i in idx..size {
587                            ffi::lua_rawgeti(state, -1, i + 1);
588                            ffi::lua_rawseti(state, -2, i);
589                        }
590                        ffi::lua_pushnil(state);
591                        ffi::lua_rawseti(state, -2, size);
592                    })
593                }
594            }
595            _ => self.raw_set(key, Nil),
596        }
597    }
598
599    /// Clears the table, removing all keys and values from array and hash parts,
600    /// without invoking metamethods.
601    ///
602    /// This method is useful to clear the table while keeping its capacity.
603    pub fn clear(&self) -> Result<()> {
604        let lua = self.0.lua.lock();
605        unsafe {
606            #[cfg(feature = "luau")]
607            {
608                self.check_readonly_write(&lua)?;
609                ffi::lua_cleartable(lua.ref_thread(), self.0.index);
610            }
611
612            #[cfg(not(feature = "luau"))]
613            {
614                let state = lua.state();
615                let _sg = StackGuard::new(state);
616                check_stack(state, 4)?;
617
618                lua.push_ref(&self.0);
619
620                // This is safe as long as we don't assign new keys
621                ffi::lua_pushnil(state);
622                while ffi::lua_next(state, -2) != 0 {
623                    ffi::lua_pop(state, 1); // pop value
624                    ffi::lua_pushvalue(state, -1); // copy key
625                    ffi::lua_pushnil(state);
626                    ffi::lua_rawset(state, -4);
627                }
628            }
629        }
630
631        Ok(())
632    }
633
634    /// Returns the result of the Lua `#` operator.
635    ///
636    /// This might invoke the `__len` metamethod. Use the [`Table::raw_len`] method if that is not
637    /// desired.
638    pub fn len(&self) -> Result<Integer> {
639        // Fast track (skip protected call)
640        if !self.has_metatable() {
641            return Ok(self.raw_len() as Integer);
642        }
643
644        let lua = self.0.lua.lock();
645        let state = lua.state();
646        unsafe {
647            let _sg = StackGuard::new(state);
648            check_stack(state, 4)?;
649
650            lua.push_ref(&self.0);
651            protect_lua!(state, 1, 0, |state| ffi::luaL_len(state, -1))
652        }
653    }
654
655    /// Returns the result of the Lua `#` operator, without invoking the `__len` metamethod.
656    pub fn raw_len(&self) -> usize {
657        let lua = self.0.lua.lock();
658        unsafe { ffi::lua_rawlen(lua.ref_thread(), self.0.index) }
659    }
660
661    /// Returns `true` if the table is empty, without invoking metamethods.
662    ///
663    /// It checks both the array part and the hash part.
664    pub fn is_empty(&self) -> bool {
665        let lua = self.0.lua.lock();
666        let ref_thread = lua.ref_thread();
667        unsafe {
668            ffi::lua_pushnil(ref_thread);
669            if ffi::lua_next(ref_thread, self.0.index) == 0 {
670                return true;
671            }
672            ffi::lua_pop(ref_thread, 2);
673        }
674        false
675    }
676
677    /// Returns a reference to the metatable of this table, or `None` if no metatable is set.
678    ///
679    /// Unlike the [`getmetatable`] Lua function, this method ignores the `__metatable` field.
680    ///
681    /// [`getmetatable`]: https://www.lua.org/manual/5.4/manual.html#pdf-getmetatable
682    pub fn metatable(&self) -> Option<Table> {
683        let lua = self.0.lua.lock();
684        let ref_thread = lua.ref_thread();
685        unsafe {
686            if ffi::lua_getmetatable(ref_thread, self.0.index) == 0 {
687                None
688            } else {
689                Some(Table(lua.pop_ref_thread()))
690            }
691        }
692    }
693
694    /// Sets or removes the metatable of this table.
695    ///
696    /// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
697    /// nothing).
698    pub fn set_metatable(&self, metatable: Option<Table>) -> Result<()> {
699        #[cfg(feature = "luau")]
700        if self.is_readonly() {
701            return Err(Error::runtime("attempt to modify a readonly table"));
702        }
703
704        let lua = self.0.lua.lock();
705        let ref_thread = lua.ref_thread();
706        unsafe {
707            if let Some(metatable) = &metatable {
708                ffi::lua_pushvalue(ref_thread, metatable.0.index);
709            } else {
710                ffi::lua_pushnil(ref_thread);
711            }
712            ffi::lua_setmetatable(ref_thread, self.0.index);
713        }
714        Ok(())
715    }
716
717    /// Returns true if the table has metatable attached.
718    #[doc(hidden)]
719    #[inline]
720    pub fn has_metatable(&self) -> bool {
721        let lua = self.0.lua.lock();
722        unsafe { !get_metatable_ptr(lua.ref_thread(), self.0.index).is_null() }
723    }
724
725    /// Sets `readonly` attribute on the table.
726    #[cfg(any(feature = "luau", doc))]
727    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
728    pub fn set_readonly(&self, enabled: bool) {
729        let lua = self.0.lua.lock();
730        let ref_thread = lua.ref_thread();
731        unsafe {
732            ffi::lua_setreadonly(ref_thread, self.0.index, enabled as _);
733            if !enabled {
734                // Reset "safeenv" flag
735                ffi::lua_setsafeenv(ref_thread, self.0.index, 0);
736            }
737        }
738    }
739
740    /// Returns `readonly` attribute of the table.
741    #[cfg(any(feature = "luau", doc))]
742    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
743    pub fn is_readonly(&self) -> bool {
744        let lua = self.0.lua.lock();
745        let ref_thread = lua.ref_thread();
746        unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
747    }
748
749    /// Controls `safeenv` attribute on the table.
750    ///
751    /// This a special flag that activates some performance optimizations for environment tables.
752    /// In particular, it controls:
753    /// - Optimization of import resolution (cache values of constant keys).
754    /// - Fast-path for built-in iteration with pairs/ipairs.
755    /// - Fast-path for some built-in functions (fastcall).
756    ///
757    /// For `safeenv` environments, monkey patching or modifying values may not work as expected.
758    #[cfg(any(feature = "luau", doc))]
759    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
760    pub fn set_safeenv(&self, enabled: bool) {
761        let lua = self.0.lua.lock();
762        unsafe { ffi::lua_setsafeenv(lua.ref_thread(), self.0.index, enabled as _) };
763    }
764
765    /// Converts this table to a generic C pointer.
766    ///
767    /// Different tables will give different pointers.
768    /// There is no way to convert the pointer back to its original value.
769    ///
770    /// Typically this function is used only for hashing and debug information.
771    #[inline]
772    pub fn to_pointer(&self) -> *const c_void {
773        self.0.to_pointer()
774    }
775
776    /// Returns an iterator over the pairs of the table.
777    ///
778    /// This works like the Lua `pairs` function, but does not invoke the `__pairs` metamethod.
779    ///
780    /// The pairs are wrapped in a [`Result`], since they are lazily converted to `K` and `V` types.
781    ///
782    /// # Examples
783    ///
784    /// Iterate over all globals:
785    ///
786    /// ```
787    /// # use mlua::{Lua, Result, Value};
788    /// # fn main() -> Result<()> {
789    /// # let lua = Lua::new();
790    /// let globals = lua.globals();
791    ///
792    /// for pair in globals.pairs::<Value, Value>() {
793    ///     let (key, value) = pair?;
794    /// #   let _ = (key, value);   // used
795    ///     // ...
796    /// }
797    /// # Ok(())
798    /// # }
799    /// ```
800    ///
801    /// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
802    pub fn pairs<K: FromLua, V: FromLua>(&self) -> TablePairs<'_, K, V> {
803        TablePairs {
804            guard: self.0.lua.lock(),
805            table: self,
806            key: Some(Nil),
807            _phantom: PhantomData,
808        }
809    }
810
811    /// Iterates over the pairs of the table, invoking the given closure on each pair.
812    ///
813    /// This method is similar to [`Table::pairs`], but optimized for performance.
814    /// It does not invoke the `__pairs` metamethod.
815    pub fn for_each<K, V>(&self, mut f: impl FnMut(K, V) -> Result<()>) -> Result<()>
816    where
817        K: FromLua,
818        V: FromLua,
819    {
820        let lua = self.0.lua.lock();
821        let state = lua.state();
822        unsafe {
823            let _sg = StackGuard::new(state);
824            check_stack(state, 5)?;
825
826            lua.push_ref(&self.0);
827            ffi::lua_pushnil(state);
828            while ffi::lua_next(state, -2) != 0 {
829                let k = K::from_stack(-2, &lua)?;
830                let v = lua.pop::<V>()?;
831                f(k, v)?;
832            }
833        }
834        Ok(())
835    }
836
837    /// Returns an iterator over all values in the sequence part of the table.
838    ///
839    /// The iterator will yield all values `t[1]`, `t[2]` and so on, until a `nil` value is
840    /// encountered. This mirrors the behavior of Lua's `ipairs` function but does not invoke
841    /// any metamethods.
842    ///
843    /// # Examples
844    ///
845    /// ```
846    /// # use mlua::{Lua, Result, Table};
847    /// # fn main() -> Result<()> {
848    /// # let lua = Lua::new();
849    /// let my_table: Table = lua.load(r#"
850    ///     {
851    ///         [1] = 4,
852    ///         [2] = 5,
853    ///         [4] = 7,
854    ///         key = 2
855    ///     }
856    /// "#).eval()?;
857    ///
858    /// let expected = [4, 5];
859    /// for (&expected, got) in expected.iter().zip(my_table.sequence_values::<u32>()) {
860    ///     assert_eq!(expected, got?);
861    /// }
862    /// # Ok(())
863    /// # }
864    /// ```
865    pub fn sequence_values<V: FromLua>(&self) -> TableSequence<'_, V> {
866        TableSequence {
867            guard: self.0.lua.lock(),
868            table: self,
869            index: 1,
870            len: None,
871            _phantom: PhantomData,
872        }
873    }
874
875    /// Iterates over the sequence part of the table, invoking the given closure on each value.
876    ///
877    /// This methods is similar to [`Table::sequence_values`], but optimized for performance.
878    #[doc(hidden)]
879    pub fn for_each_value<V: FromLua>(&self, f: impl FnMut(V) -> Result<()>) -> Result<()> {
880        self.for_each_value_by_len(None, f)
881    }
882
883    fn for_each_value_by_len<V: FromLua>(
884        &self,
885        len: impl Into<Option<usize>>,
886        mut f: impl FnMut(V) -> Result<()>,
887    ) -> Result<()> {
888        let len = len.into();
889        let lua = self.0.lua.lock();
890        let state = lua.state();
891        unsafe {
892            let _sg = StackGuard::new(state);
893            check_stack(state, 4)?;
894
895            lua.push_ref(&self.0);
896            for i in 1.. {
897                if len.map(|len| i > len).unwrap_or(false) {
898                    break;
899                }
900                let t = ffi::lua_rawgeti(state, -1, i as _);
901                if len.is_none() && t == ffi::LUA_TNIL {
902                    break;
903                }
904                f(lua.pop::<V>()?)?;
905            }
906        }
907        Ok(())
908    }
909
910    /// Sets element value at position `idx` without invoking metamethods.
911    #[doc(hidden)]
912    pub fn raw_seti(&self, idx: usize, value: impl IntoLua) -> Result<()> {
913        let lua = self.0.lua.lock();
914        let state = lua.state();
915        unsafe {
916            #[cfg(feature = "luau")]
917            self.check_readonly_write(&lua)?;
918
919            let _sg = StackGuard::new(state);
920            check_stack(state, 5)?;
921
922            lua.push_ref(&self.0);
923            value.push_into_stack(&lua)?;
924
925            let idx = idx.try_into().unwrap();
926            if lua.unlikely_memory_error() {
927                ffi::lua_rawseti(state, -2, idx);
928            } else {
929                protect_lua!(state, 2, 0, |state| ffi::lua_rawseti(state, -2, idx))?;
930            }
931        }
932        Ok(())
933    }
934
935    /// Checks if the table has the array metatable attached.
936    #[cfg(feature = "serde")]
937    fn has_array_metatable(&self) -> bool {
938        let lua = self.0.lua.lock();
939        let state = lua.state();
940        unsafe {
941            let _sg = StackGuard::new(state);
942            assert_stack(state, 3);
943
944            lua.push_ref(&self.0);
945            if ffi::lua_getmetatable(state, -1) == 0 {
946                return false;
947            }
948            crate::serde::push_array_metatable(state);
949            ffi::lua_rawequal(state, -1, -2) != 0
950        }
951    }
952
953    /// If the table is an array, returns the number of non-nil elements and max index.
954    ///
955    /// Returns `None` if the table is not an array.
956    ///
957    /// This operation has O(n) complexity.
958    #[cfg(feature = "serde")]
959    fn find_array_len(&self) -> Option<(usize, usize)> {
960        let lua = self.0.lua.lock();
961        let ref_thread = lua.ref_thread();
962        unsafe {
963            let _sg = StackGuard::new(ref_thread);
964
965            let (mut count, mut max_index) = (0, 0);
966            ffi::lua_pushnil(ref_thread);
967            while ffi::lua_next(ref_thread, self.0.index) != 0 {
968                if ffi::lua_type(ref_thread, -2) != ffi::LUA_TNUMBER {
969                    return None;
970                }
971
972                let k = ffi::lua_tonumber(ref_thread, -2);
973                if k.trunc() != k || k < 1.0 {
974                    return None;
975                }
976                max_index = std::cmp::max(max_index, k as usize);
977                count += 1;
978                ffi::lua_pop(ref_thread, 1);
979            }
980            Some((count, max_index))
981        }
982    }
983
984    /// Determines if the table should be encoded as an array or a map.
985    ///
986    /// The algorithm is the following:
987    /// 1. If the table has the array metatable attached, always encode it as an array.
988    ///
989    /// 2. If `detect_mixed_tables` is enabled, iterate over all keys in the table checking is they
990    ///    all are positive integers. If non-array key is found, return `None` (encode as map).
991    ///    Otherwise check the sparsity of the array. Too sparse arrays are encoded as maps.
992    ///
993    /// 3. If `detect_mixed_tables` is disabled, check if the table has a positive length. If so,
994    ///    encode as array. If the table is empty and `encode_empty_tables_as_array` is enabled,
995    ///    encode as array.
996    ///
997    /// Returns the length of the array if it should be encoded as an array.
998    #[cfg(feature = "serde")]
999    pub(crate) fn encode_as_array(&self, options: crate::serde::de::Options) -> Option<usize> {
1000        if self.has_array_metatable() {
1001            return Some(self.raw_len());
1002        }
1003        if options.detect_mixed_tables {
1004            if let Some((len, max_idx)) = self.find_array_len() {
1005                // If the array is too sparse, serialize it as a map instead
1006                if len < 10 || len * 2 >= max_idx {
1007                    return Some(max_idx);
1008                }
1009            }
1010        } else {
1011            let len = self.raw_len();
1012            if len > 0 {
1013                return Some(len);
1014            }
1015            if options.encode_empty_tables_as_array && self.is_empty() {
1016                return Some(0);
1017            }
1018        }
1019        None
1020    }
1021
1022    #[cfg(feature = "luau")]
1023    #[inline(always)]
1024    fn check_readonly_write(&self, lua: &RawLua) -> Result<()> {
1025        if unsafe { ffi::lua_getreadonly(lua.ref_thread(), self.0.index) != 0 } {
1026            return Err(Error::runtime("attempt to modify a readonly table"));
1027        }
1028        Ok(())
1029    }
1030
1031    pub(crate) fn fmt_pretty(
1032        &self,
1033        fmt: &mut fmt::Formatter,
1034        ident: usize,
1035        visited: &mut HashSet<*const c_void>,
1036    ) -> fmt::Result {
1037        visited.insert(self.to_pointer());
1038
1039        // Collect key/value pairs into a vector so we can sort them
1040        let mut pairs = self.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
1041        // Sort keys
1042        pairs.sort_by(|(a, _), (b, _)| a.sort_cmp(b));
1043        let is_sequence = (pairs.iter().enumerate())
1044            .all(|(i, (k, _))| matches!(k, Value::Integer(n) if *n == (i + 1) as Integer));
1045        if pairs.is_empty() {
1046            return write!(fmt, "{{}}");
1047        }
1048        writeln!(fmt, "{{")?;
1049        if is_sequence {
1050            // Format as list
1051            for (_, value) in pairs {
1052                write!(fmt, "{}", " ".repeat(ident + 2))?;
1053                value.fmt_pretty(fmt, true, ident + 2, visited)?;
1054                writeln!(fmt, ",")?;
1055            }
1056        } else {
1057            fn is_simple_key(key: &[u8]) -> bool {
1058                key.iter().take(1).all(|c| c.is_ascii_alphabetic() || *c == b'_')
1059                    && key.iter().all(|c| c.is_ascii_alphanumeric() || *c == b'_')
1060            }
1061
1062            for (key, value) in pairs {
1063                match key {
1064                    Value::String(key) if is_simple_key(&key.as_bytes()) => {
1065                        write!(fmt, "{}{}", " ".repeat(ident + 2), key.display())?;
1066                        write!(fmt, " = ")?;
1067                    }
1068                    _ => {
1069                        write!(fmt, "{}[", " ".repeat(ident + 2))?;
1070                        key.fmt_pretty(fmt, false, ident + 2, visited)?;
1071                        write!(fmt, "] = ")?;
1072                    }
1073                }
1074                value.fmt_pretty(fmt, true, ident + 2, visited)?;
1075                writeln!(fmt, ",")?;
1076            }
1077        }
1078        write!(fmt, "{}}}", " ".repeat(ident))
1079    }
1080}
1081
1082impl fmt::Debug for Table {
1083    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
1084        if fmt.alternate() {
1085            return self.fmt_pretty(fmt, 0, &mut HashSet::new());
1086        }
1087        fmt.debug_tuple("Table").field(&self.0).finish()
1088    }
1089}
1090
1091impl<T> PartialEq<[T]> for Table
1092where
1093    T: IntoLua + Clone,
1094{
1095    fn eq(&self, other: &[T]) -> bool {
1096        let lua = self.0.lua.lock();
1097        let state = lua.state();
1098        unsafe {
1099            let _sg = StackGuard::new(state);
1100            assert_stack(state, 4);
1101
1102            lua.push_ref(&self.0);
1103
1104            let len = ffi::lua_rawlen(state, -1);
1105            for i in 0..len {
1106                ffi::lua_rawgeti(state, -1, (i + 1) as _);
1107                let val = lua.pop_value();
1108                if val == Nil {
1109                    return i == other.len();
1110                }
1111                match other.get(i).map(|v| v.clone().into_lua(lua.lua())) {
1112                    Some(Ok(other_val)) if val == other_val => continue,
1113                    _ => return false,
1114                }
1115            }
1116        }
1117        true
1118    }
1119}
1120
1121impl<T> PartialEq<&[T]> for Table
1122where
1123    T: IntoLua + Clone,
1124{
1125    #[inline]
1126    fn eq(&self, other: &&[T]) -> bool {
1127        self == *other
1128    }
1129}
1130
1131impl<T, const N: usize> PartialEq<[T; N]> for Table
1132where
1133    T: IntoLua + Clone,
1134{
1135    #[inline]
1136    fn eq(&self, other: &[T; N]) -> bool {
1137        self == &other[..]
1138    }
1139}
1140
1141impl ObjectLike for Table {
1142    #[inline]
1143    fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
1144        self.get(key)
1145    }
1146
1147    #[inline]
1148    fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
1149        self.set(key, value)
1150    }
1151
1152    #[inline]
1153    fn call<R>(&self, args: impl IntoLuaMulti) -> Result<R>
1154    where
1155        R: FromLuaMulti,
1156    {
1157        // Convert table to a function and call via pcall that respects the `__call` metamethod.
1158        Function(self.0.clone()).call(args)
1159    }
1160
1161    #[cfg(feature = "async")]
1162    #[inline]
1163    fn call_async<R>(&self, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
1164    where
1165        R: FromLuaMulti,
1166    {
1167        Function(self.0.clone()).call_async(args)
1168    }
1169
1170    #[inline]
1171    fn call_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
1172    where
1173        R: FromLuaMulti,
1174    {
1175        self.call_function(name, (self, args))
1176    }
1177
1178    #[cfg(feature = "async")]
1179    fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
1180    where
1181        R: FromLuaMulti,
1182    {
1183        self.call_async_function(name, (self, args))
1184    }
1185
1186    #[inline]
1187    fn call_function<R: FromLuaMulti>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R> {
1188        match self.get(name)? {
1189            Value::Function(func) => func.call(args),
1190            val => {
1191                let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
1192                Err(Error::runtime(msg))
1193            }
1194        }
1195    }
1196
1197    #[cfg(feature = "async")]
1198    #[inline]
1199    fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> AsyncCallFuture<R>
1200    where
1201        R: FromLuaMulti,
1202    {
1203        match self.get(name) {
1204            Ok(Value::Function(func)) => func.call_async(args),
1205            Ok(val) => {
1206                let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
1207                AsyncCallFuture::error(Error::RuntimeError(msg))
1208            }
1209            Err(err) => AsyncCallFuture::error(err),
1210        }
1211    }
1212
1213    #[inline]
1214    fn to_string(&self) -> Result<String> {
1215        Value::Table(Table(self.0.clone())).to_string()
1216    }
1217
1218    #[inline]
1219    fn to_value(&self) -> Value {
1220        Value::Table(self.clone())
1221    }
1222
1223    #[inline]
1224    fn weak_lua(&self) -> &WeakLua {
1225        &self.0.lua
1226    }
1227}
1228
1229/// A wrapped [`Table`] with customized serialization behavior.
1230#[cfg(feature = "serde")]
1231pub(crate) struct SerializableTable<'a> {
1232    table: &'a Table,
1233    options: crate::serde::de::Options,
1234    visited: Rc<RefCell<FxHashSet<*const c_void>>>,
1235}
1236
1237#[cfg(feature = "serde")]
1238impl Serialize for Table {
1239    #[inline]
1240    fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
1241        SerializableTable::new(self, Default::default(), Default::default()).serialize(serializer)
1242    }
1243}
1244
1245#[cfg(feature = "serde")]
1246impl<'a> SerializableTable<'a> {
1247    #[inline]
1248    pub(crate) fn new(
1249        table: &'a Table,
1250        options: crate::serde::de::Options,
1251        visited: Rc<RefCell<FxHashSet<*const c_void>>>,
1252    ) -> Self {
1253        Self {
1254            table,
1255            options,
1256            visited,
1257        }
1258    }
1259}
1260
1261impl<V> TableSequence<'_, V> {
1262    /// Sets the length (hint) of the sequence.
1263    #[cfg(feature = "serde")]
1264    pub(crate) fn with_len(mut self, len: usize) -> Self {
1265        self.len = Some(len);
1266        self
1267    }
1268}
1269
1270#[cfg(feature = "serde")]
1271impl Serialize for SerializableTable<'_> {
1272    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
1273    where
1274        S: Serializer,
1275    {
1276        use crate::serde::de::{MapPairs, RecursionGuard, check_value_for_skip};
1277        use crate::value::SerializableValue;
1278
1279        let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res {
1280            Ok(v) => Ok(v),
1281            Err(Error::SerializeError(_)) if serialize_err.is_some() => Err(serialize_err.unwrap()),
1282            Err(Error::SerializeError(msg)) => Err(serde::ser::Error::custom(msg)),
1283            Err(err) => Err(serde::ser::Error::custom(err.to_string())),
1284        };
1285
1286        let options = self.options;
1287        let visited = &self.visited;
1288        let _guard = RecursionGuard::new(self.table, visited);
1289
1290        // Array
1291        if let Some(len) = self.table.encode_as_array(self.options) {
1292            let mut seq = serializer.serialize_seq(Some(len))?;
1293            let mut serialize_err = None;
1294            let res = self.table.for_each_value_by_len::<Value>(len, |value| {
1295                let skip = check_value_for_skip(&value, self.options, visited)
1296                    .map_err(|err| Error::SerializeError(err.to_string()))?;
1297                if skip {
1298                    // continue iteration
1299                    return Ok(());
1300                }
1301                seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
1302                    .map_err(|err| {
1303                        serialize_err = Some(err);
1304                        Error::SerializeError(String::new())
1305                    })
1306            });
1307            convert_result(res, serialize_err)?;
1308            return seq.end();
1309        }
1310
1311        // HashMap
1312        let mut map = serializer.serialize_map(None)?;
1313        let mut serialize_err = None;
1314        let mut process_pair = |key, value| {
1315            let skip_key = check_value_for_skip(&key, self.options, visited)
1316                .map_err(|err| Error::SerializeError(err.to_string()))?;
1317            let skip_value = check_value_for_skip(&value, self.options, visited)
1318                .map_err(|err| Error::SerializeError(err.to_string()))?;
1319            if skip_key || skip_value {
1320                // continue iteration
1321                return Ok(());
1322            }
1323            map.serialize_entry(
1324                &SerializableValue::new(&key, options, Some(visited)),
1325                &SerializableValue::new(&value, options, Some(visited)),
1326            )
1327            .map_err(|err| {
1328                serialize_err = Some(err);
1329                Error::SerializeError(String::new())
1330            })
1331        };
1332
1333        let res = if !self.options.sort_keys {
1334            // Fast track
1335            self.table.for_each(process_pair)
1336        } else {
1337            MapPairs::new(self.table, self.options.sort_keys)
1338                .map_err(serde::ser::Error::custom)?
1339                .try_for_each(|kv| {
1340                    let (key, value) = kv?;
1341                    process_pair(key, value)
1342                })
1343        };
1344        convert_result(res, serialize_err)?;
1345        map.end()
1346    }
1347}
1348
1349/// An iterator over the pairs of a Lua table.
1350///
1351/// This struct is created by the [`Table::pairs`] method.
1352///
1353/// [`Table::pairs`]: crate::Table::pairs
1354pub struct TablePairs<'a, K, V> {
1355    guard: LuaGuard,
1356    table: &'a Table,
1357    key: Option<Value>,
1358    _phantom: PhantomData<(K, V)>,
1359}
1360
1361impl<K, V> Iterator for TablePairs<'_, K, V>
1362where
1363    K: FromLua,
1364    V: FromLua,
1365{
1366    type Item = Result<(K, V)>;
1367
1368    fn next(&mut self) -> Option<Self::Item> {
1369        if let Some(prev_key) = self.key.take() {
1370            let lua: &RawLua = &self.guard;
1371            let state = lua.state();
1372
1373            let res = (|| unsafe {
1374                let _sg = StackGuard::new(state);
1375                check_stack(state, 5)?;
1376
1377                lua.push_ref(&self.table.0);
1378                lua.push_value(&prev_key)?;
1379
1380                // It must be safe to call `lua_next` unprotected as deleting a key from a table is
1381                // a permitted operation.
1382                // It fails only if the key is not found (never existed) which seems impossible scenario.
1383                if ffi::lua_next(state, -2) != 0 {
1384                    let key = lua.stack_value(-2, None);
1385                    Ok(Some((
1386                        key.clone(),
1387                        K::from_lua(key, lua.lua())?,
1388                        V::from_stack(-1, lua)?,
1389                    )))
1390                } else {
1391                    Ok(None)
1392                }
1393            })();
1394
1395            match res {
1396                Ok(Some((key, ret_key, value))) => {
1397                    self.key = Some(key);
1398                    Some(Ok((ret_key, value)))
1399                }
1400                Ok(None) => None,
1401                Err(e) => Some(Err(e)),
1402            }
1403        } else {
1404            None
1405        }
1406    }
1407}
1408
1409/// An iterator over the sequence part of a Lua table.
1410///
1411/// This struct is created by the [`Table::sequence_values`] method.
1412///
1413/// [`Table::sequence_values`]: crate::Table::sequence_values
1414pub struct TableSequence<'a, V> {
1415    guard: LuaGuard,
1416    table: &'a Table,
1417    index: Integer,
1418    len: Option<usize>,
1419    _phantom: PhantomData<V>,
1420}
1421
1422impl<V: FromLua> Iterator for TableSequence<'_, V> {
1423    type Item = Result<V>;
1424
1425    fn next(&mut self) -> Option<Self::Item> {
1426        let lua: &RawLua = &self.guard;
1427        let state = lua.state();
1428        unsafe {
1429            let _sg = StackGuard::new(state);
1430            if let Err(err) = check_stack(state, 1) {
1431                return Some(Err(err));
1432            }
1433
1434            lua.push_ref(&self.table.0);
1435            match ffi::lua_rawgeti(state, -1, self.index) {
1436                ffi::LUA_TNIL if self.index as usize > self.len.unwrap_or(0) => None,
1437                _ => {
1438                    self.index += 1;
1439                    Some(V::from_stack(-1, lua))
1440                }
1441            }
1442        }
1443    }
1444}
1445
1446#[cfg(test)]
1447mod assertions {
1448    use super::*;
1449
1450    #[cfg(not(feature = "send"))]
1451    static_assertions::assert_not_impl_any!(Table: Send);
1452    #[cfg(feature = "send")]
1453    static_assertions::assert_impl_all!(Table: Send, Sync);
1454}