(This quote seems to no longer appear in your comment, but I started drafting my reply about five hours ago.)

> - Do not repeat yourself.

I recently ran into a case where DRY was practically a necessity, because the alternative (of simply repeating myself) would not only have been prone to copy/paste errors during authorship, but also have resulted in code with an incredibly low signal to noise ratio (which also would have made authorship quite tedious).

For a bit of backstory, I'm currently toying with writing some Luau bindings for Rust, but Luau (by default) throws C++ exceptions instead of using longjmp. These exceptions can be thrown by almost any Luau function, and I need to prevent them from unwinding across FFI boundaries to avoid triggering Undefined Behavior, because in Rust, it is UB for a "C"-ABI function to unwind into its caller.

Here are some things that can throw C++ exceptions in Luau:

- OOM when allocating new garbage-collectible objects in an existing VM. Creating the VM itself is (most traditionally) done through luaL_newstate, which returns NULL on allocation failure, but once you have a VM, attempting to instantiate a collectible object within it will throw an exception if allocation fails. This can happen indirectly through any other operation that internally creates collectible objects.

- Attempting to modify a readonly table. Luau implements a readonly bit for tables that controls a guard against modification of that table, even by native code. It does this by throwing an exception when any such modification is attempted. However, just like collectible objects, table modification is an implementation detail of all sorts of things. While I don't expect anyone to be able to readonly the Luau registry, it is still a thing that you can do to arbitrary other tables (assuming that the host side doesn't omit or remove the `table.freeze` function from the standard library). Technically, you could defensively check the readonly bit from Rust before attempting those operations, but that would cause it to be checked twice on each operation, and this workaround doesn't generalize to other sources of exceptions, nor does it work through indirections (random other functions that happen to write to tables internally).

- Arbitrary function calls, if an interrupt callback is set. Luau supports an "interrupt" callback that is periodically called by the VM at "checkpoints" such as function calls. The interrupt callback is allowed to throw an exception, as this is the officially supported method of eventually terminating functions that contain infinite loops or other code that blocks the thread for too long. AFAIK, it can also be triggered in the middle of C closures if you perform certain operations like making a Luau function call. Although Luau function calls themselves may error, the interrupt callback can cause any Luau function call to error, even if it normally should not.

Luau probably expects those kinds of operations to be performed primarily by C++ functions executing under the VM ("C closures"), where any exceptions will be automatically caught and a status code returned to whoever called the VM. Generally, it is quite a reasonable assumption to make that a library will be used from the same language that it is written in.

However, Rust can't currently express "C closures" that are capable of unwinding, because unwinding out of a "C"-ABI function is Undefined Behavior (Rust wouldn't be able to unwind into the VM), and so is a "C"-ABI function unwinding into a Rust stack frame (library functions wouldn't be able to unwind into Rust in the first place).

The "C-unwind" ABI should eventually allow Rust to express and call FFI functions that are capable of unwinding, but it hasn't quite reached stabilization yet.

If Rust had already supported "C-unwind", I could have just defined a C++ wrapper function that catches Luau exceptions and translates them into a tagged union or something representing success or failure. Unfortunately that is not yet the case.

Luckily, Luau has some internal functions that allow you to catch exceptions regardless of whether they are exceptions or longjmps, but they work by executing a callback that you pass to it, and you can only pass a single pointer value through to the callback. Therefore, my first solution was to place the arguments to be passed in a struct, as well as a slot for a return value, and then read that struct by pointer in order to call the function and write its return value into the slot.[0]

This worked, but it was a lot of manual code for each individual function that had to be protected from errors - you had to duplicate the argument list about 4 times (wrapper function signature, struct definition, struct initializer, and wrapped function invocation), which quickly got quite tedious, especially since quite a few functions are going to need this wrapping treatment.

My first DRY implementation[1] was to create a template that can produce a wrapper around any function, that calls it with any number of arguments, of any types. I still needed to manually prepare a list of functions in advance to protect, since you can't instantiate templates over FFI, but this was a lot more painless than the first try.

It's just that the templates are a bit of a mess.

[0]: https://github.com/LoganDark/luau-rs/blob/787eb0c7987c08304c...

[1]: https://github.com/LoganDark/luau-rs/blob/de5abd5420a5311c27...