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