Library Extensions
Luwu's standard library is Luau's plus a few additions that fix real problems.
table.drop
table.drop removes items from a list by value. It returns how many it removed.
It exists to fix one of Luau's most common bugs.
The table.remove footgun
Luau added table.find to get the index of a value in a list. Pairing it with table.remove looks like the obvious way to remove a value:
local party = { "Ada", "Grace", "Alan" }
table.remove(party, table.find(party, "Linus"))
print(#party, party[3]) -- 2, nil
table.find gives nil for Linus, who isn't in the party. Given nil as its index, table.remove removes the last item instead. Alan is gone, and nothing reports an error.
The bug only shows up when the value is missing. That makes it easy to miss in testing. Luau doesn't warn about it. The safe version needs an if around every removal.
Removing by value
table.drop takes the value itself. A missing value removes nothing:
const party = { "Ada", "Grace", "Alan" }
print(table.drop(party, "Linus"), #party)
print(table.drop(party, "Grace"), #party)
The return value says how many items were removed. Check it when a missing value matters:
const inventory = { "sword", "potion" }
if table.drop(inventory, "key") == 0 then
print("You don't have a key.")
end
By default, every match is removed. Pass a count to remove only the first few:
const queue = { "retry", "retry", "retry", "done" }
table.drop(queue, "retry", 2)
print(#queue, queue[1], queue[2])
The lint
Luwu also warns when table.remove gets an index that might be nil. The old pattern is underlined with this message:
If this is `nil`, `table.remove` will remove the last element of the array.
Consider using `table.drop` instead. If order is not important, use a key/value table for better performance.
Passing nil on purpose, like table.remove(t, nil), doesn't warn.
Things to know
table.dropcompares values liketable.find, including__eq.- The items after a removed one shift down. The list never gets holes.
- Dropping from a read-only table is an error. So is a count of zero or less.
- If order doesn't matter, a set (a table of values mapped to true) removes faster.
buffer.isfrozen
Programs that embed Luwu can hand it read-only buffers. buffer.isfrozen tells you whether a buffer is one of them.
Writing to a frozen buffer is an error. Buffers you create in Luwu are never frozen:
const bytes = buffer.create(8)
buffer.writeu8(bytes, 0, 255)
print(buffer.isfrozen(bytes))
Only the runtime can freeze a buffer, by creating it as a read-only external buffer.
The class library
The class library works with classes and objects:
class.isinstance(value, Class)checks whether a value is an object of a class.class.implements(value, Trait)checks whether its class implements a trait.class.of(object)gives an object's class.class.name(Class)gives a class's name.class.fields(object)gives an object's public fields as a table.
They're covered with examples on the Classes and Traits page.
Planned additions
We want to add these functions next:
--- Counts every element in the array and hash parts, to get the size of a map-like or struct-like table.
table.count<K, V>(t: { read [K]: V }): number
--- Copies every element of e onto the end of t, changing t.
table.extend<T>(t: { T }, e: { T })
--- A new table with the elements of t, then the elements of e.
table.combine<T, U>(t: { read T }, e: { read U }): { T | U }
--- An array of the table's keys.
table.keys<K, V>(t: { read [K]: V }): { K }
--- An array of the table's values.
table.values<K, V>(t: { read [K]: V }): { V }
--- Whether s starts with prefix. Not pattern aware or unicode aware.
string.startswith(s: string, prefix: string): boolean
--- Whether s ends with suffix. Not pattern aware or unicode aware.
string.endswith(s: string, suffix: string): boolean
--- Trims whitespace from the start, the end or both ("all", the default).
string.trim(s: string, mode: ("start" | "end" | "all")?): string
--- Whether s contains substring, without patterns, like string.find(s, substring, 1, true).
string.contains(s: string, substring: string): boolean