Low level but safe bindings to the LuaJIT C API with no dependencies and no-std support. https://crates.io/crates/low-luajit
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
2026-10-03 17:21:25 +02:00
examples Make LuaState::is_function_upvalue and LuaState::upvalue_id only require &self instead of &mut self. 2026-10-02 21:38:26 +02:00
src Tiny internal struct refactor. 2026-10-03 12:51:38 +02:00
.gitignore ok we're back on track 2026-06-13 00:08:51 +02:00
Cargo.lock Update version. 2026-10-03 17:21:25 +02:00
Cargo.toml Update version. 2026-10-03 17:21:25 +02:00
LICENSE initial commit 2026-06-06 09:55:32 +02:00
README.md Tiny doc fixes. 2026-09-23 19:53:55 +02:00

low-luajit

No AI crates.io downloads docs.rs MSRV

A lightweight library that aims to add low level but safe (as much as possible) bindings to the LuaJIT C API with no dependencies and no-std support.

These bindings can be used to create dynamic Rust libraries (.dll/.so) that can be loaded and ran from LuaJIT like any other Lua module.

use low_luajit::{LuaState, AmountReturned};

extern "C-unwind" fn multiply(mut lua: LuaState) -> AmountReturned {
	let x = lua.check_number(1);
	let y = lua.check_number(2);

	lua.return_number(x * y);
}

#[unsafe(no_mangle)]
extern "C-unwind" fn luaopen_mymodule(mut lua: LuaState) -> AmountReturned {
	lua.return_cfunction(multiply)
}
local multiply = require("mymodule")

print(multiply(52, 196))

For more information, check out the documentation and the examples directory.

Compatibility & Maintenance

This library is used internally for BlossomDevs games, so it should stay maintained for at least a very long time. Version 1.0 will be reached once things feel stable, comfortable and tested enough.

LuaJIT wise, the objective is currently to support v2.0 and v2.1's LuaJIT C API, as it should not receive any breaking changes. If LuaJIT v3.0 is released...support will be added based on the severity of the breaking changes (most likely via a cargo feature).

Anything LuaJIT officially supports (i.e. which devices and CPU architectures are supported) should be compatible. While anything not officially supported will be disregarded and may or may not be compatible.

Why only official LuaJIT?

Simply because it's the Lua I personally use and know best, I wouldn't feel qualified to add proper support for anything else.

Feel free to fork low-luajit and create your own library to support other versions/variants of Lua if needed! (Just remember to follow the licensing conditions...)

Safety policy

Ideally, every safe function in this library should not directly cause any undefined behavior (excluding external factors coming from Lua itself, like an unsafe metamethod being triggered or a function using the ffi library).

Additionally, it should be reasonably safe for the users of your module to use it and its output. (LuaJIT itself is not fully safe so at worst your module is as unsafe as any Lua module can be...)

Since the LuaJIT C API is rather unsafe, there could sadly always be some unsafe bug hidden somewhere. Report any such bug and I'll try to fix it right away!

If needed, it's possible to access the raw unsafe LuaJIT C API directly via the ffi module.

Alternatives

Below are alternatives to low-luajit alongside non-exhaustive lists of differences I've noted.

mlua

mlua is a set of high level bindings to all sorts of Lua versions (including LuaJIT), with its own custom Rust-like API that does all the inner workings for you as safely as possible.

  • low-luajit prefers to give you direct access to the C API itself (safely): while in mlua most functions tend to have quite the overhead (imo) of extra stuff inbetween you and the C API, most low-luajit methods map directly to one (sometimes more than one) C API function with as little overhead as needed to make it safe.
  • Since mlua has a far grander scope, it also has a (relatively) larger size: it has ~9x more source lines of code and the package is ~6x larger.
  • Safety wise, things are overall the same: most the unsafety comes from the Lua side of things. (We can't control what the Lua code does!)
  • While mlua supports LuaJIT, it does not have access to any of the extensions the LuaJIT C API has over the Lua C API. low-luajit instead supports the entire LuaJIT C API (safe versions of some of the extensions are currently WIP).
  • low-luajit has a far simpler and more purely functional API while mlua offers a more advanced high level API.
  • mlua supports async/await. low-luajit currently does not.
  • mlua supports serializing/deserializing any supporting type into Lua values. low-luajit instead just lets you push/pop and read Lua values directly from the stack.
  • mlua is licensed under the MIT license, which requires including the copyright and permission notice in all derivative works. low-luajit is instead licensed under the Boost Software License, which (while similar) allows sharing derivative works with no requirements when only the binary is shared. (See the licenses for more info, I'm not a lawyer and can't give legal advice.)

While both libraries can be used for the same objective, they do so in very different but equally valid ways. I do not believe either is "superior" to the other, instead I recommend doing your personal comparisons to find which one fits your needs.

LuaJIT's ffi library

The ffi library provided by LuaJIT allows calling external C functions or using C data structures from within Lua itself, by simply parsing plain C declarations.

  • It's already included in LuaJIT, so you wouldn't need to install it.
  • ffi interfaces with C code, not Rust code: you'll have to manually keep your bindings safe!
  • While modules made in low-luajit are shared as a single dynamic library (.dll/.so), modules created with ffi will need to include an additional .lua file that contains the bindings themselves.
  • The C API is unavailable to ffi, which greatly alters how interactions with Lua happen: instead of your module having direct access to the values sent by Lua, said values will instead have to be converted to C values before they can be passed. (A major example is that you have no direct access to tables, they would all need to be converted to either an array or a struct with predefined fields.)
  • low-luajit's performance compared to ffi would depend on how often the C API gets called, which will be different for every module and every function: if the module heavily relies on the C API often, it might result in less performance than ffi; on the other hand, if the module has an average usage of the C API (like simply getting the parameters and returning the results), it could even outperform ffi. (Do note that both LuaJIT and C/Rust are really fast all the time, the speed difference will likely be very negligible either way!)

Overall, low-luajit and the ffi library are very different in how they operate, but either could be used to create your module in their own way. The main thing to consider is if you'd rather interface with Lua by using the C API or by effectively creating your own custom interface.

Licensing

Copyright © 2026 Rosy.iso rosy.iso@blossomdevs.com

This library is licensed under the Boost Software License.

Contributing

Currently, feel free to email me (or send via any other means) a patch to contribute!

If enough people seem interested in this library, I will host a mirror on codeberg.

All contributions are assumed to be under the same license as the repository itself and must contain no AI generated/"assisted" code whatsoever.