Add `Table::clear()` method

This commit is contained in:
Alex Orlenko 2022-12-31 16:49:33 +00:00
parent 72b9209ae9
commit fa46720f5a
No known key found for this signature in database
GPG Key ID: 4C150C250863B96D
2 changed files with 88 additions and 0 deletions

View File

@ -414,6 +414,47 @@ impl<'lua> Table<'lua> {
}
}
/// Clears the table, removing all keys and values from array and hash parts,
/// without invoking metamethods.
///
/// This method is useful to clear the table while keeping its capacity.
pub fn clear(&self) -> Result<()> {
#[cfg(feature = "luau")]
self.check_readonly_write()?;
let lua = self.0.lua;
unsafe {
#[cfg(feature = "luau")]
ffi::lua_cleartable(lua.ref_thread(), self.0.index);
#[cfg(not(feature = "luau"))]
{
let state = lua.state();
check_stack(state, 4)?;
lua.push_ref(&self.0);
// Clear array part
for i in 1..=ffi::lua_rawlen(state, -1) {
ffi::lua_pushnil(state);
ffi::lua_rawseti(state, -2, i as Integer);
}
// Clear hash part
// It must be safe as long as we don't use invalid keys
ffi::lua_pushnil(state);
while ffi::lua_next(state, -2) != 0 {
ffi::lua_pop(state, 1); // pop value
ffi::lua_pushvalue(state, -1); // copy key
ffi::lua_pushnil(state);
ffi::lua_rawset(state, -4);
}
}
}
Ok(())
}
/// Returns the result of the Lua `#` operator.
///
/// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.

View File

@ -152,6 +152,53 @@ fn test_table_push_pop() -> Result<()> {
Ok(())
}
#[test]
fn test_table_clear() -> Result<()> {
let lua = Lua::new();
// Check readonly error
#[cfg(feature = "luau")]
{
let t = lua.create_table()?;
t.set_readonly(true);
assert!(matches!(
t.clear(),
Err(Error::RuntimeError(err)) if err.contains("attempt to modify a readonly table")
));
}
let t = lua.create_table()?;
// Set array and hash parts
t.push("abc")?;
t.push("bcd")?;
t.set("a", "1")?;
t.set("b", "2")?;
t.clear()?;
assert_eq!(t.len()?, 0);
assert_eq!(t.pairs::<Value, Value>().count(), 0);
// Test table with metamethods
let t2 = lua
.load(
r#"
setmetatable({1, 2, 3, a = "1"}, {
__index = function() error("index error") end,
__newindex = function() error("newindex error") end,
__len = function() error("len error") end,
__pairs = function() error("pairs error") end,
})
"#,
)
.eval::<Table>()?;
assert_eq!(t2.raw_len(), 3);
t2.clear()?;
assert_eq!(t2.raw_len(), 0);
assert_eq!(t2.raw_get::<_, Value>("a")?, Value::Nil);
assert_ne!(t2.get_metatable(), None);
Ok(())
}
#[test]
fn test_table_sequence_from() -> Result<()> {
let lua = Lua::new();