Skip to content

Commit 7ddcdc1

Browse files
committed
Add Table::remove
1 parent 6b19eeb commit 7ddcdc1

2 files changed

Lines changed: 80 additions & 0 deletions

File tree

src/table.rs

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,54 @@ impl Table {
341341
}
342342
}
343343

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+
344392
/// Compares two tables for equality.
345393
///
346394
/// Tables are compared by reference first.

tests/table.rs

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,38 @@ fn test_table_insert_remove() -> Result<()> {
142142
Ok(())
143143
}
144144

145+
#[test]
146+
fn test_table_remove_metatable() -> Result<()> {
147+
let lua = Lua::new();
148+
149+
let inner = lua.create_sequence_from([1, 2, 3, 4, 5])?;
150+
let mt = lua.create_table()?;
151+
mt.set("__index", &inner)?;
152+
mt.set("__newindex", &inner)?;
153+
mt.set("__len", {
154+
let inner = inner.clone();
155+
lua.create_function(move |_, ()| Ok(inner.raw_len()))?
156+
})?;
157+
158+
let t = lua.create_table()?;
159+
t.set_metatable(Some(mt))?;
160+
161+
t.remove(2)?; // removes value `2`
162+
assert_eq!(t.len()?, 4);
163+
assert_eq!(
164+
inner.pairs().collect::<Result<Vec<(i64, i64)>>>()?,
165+
vec![(1, 1), (2, 3), (3, 4), (4, 5)]
166+
);
167+
168+
// Remove non-integer key
169+
t.set("abc", "abcdef")?;
170+
assert_eq!(inner.get::<String>("abc")?, "abcdef");
171+
t.remove("abc")?;
172+
assert_eq!(inner.get::<Value>("abc")?, Value::Nil);
173+
174+
Ok(())
175+
}
176+
145177
#[test]
146178
fn test_table_clear() -> Result<()> {
147179
let lua = Lua::new();

0 commit comments

Comments
 (0)