Library Extensions

Luwu adds a few functions to the standard library it shares with Luau.

table.drop

table.drop removes items from an array by value and returns how many it removed.

It fixes one of the most common bugs in Luau code.

The table.remove footgun

Luau added table.find to get the index of a value in an array. 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.

Because the bug only shows up when the value is missing, it's easy to miss in testing. Luau doesn't warn about it and 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

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))

A buffer is frozen only when the runtime creates it as a read-only external buffer.

The class library

The class library works with classes and objects:

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