-
Notifications
You must be signed in to change notification settings - Fork 64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mapping a Lua function over a Rust Iterator #56
Comments
This is kind of a long response and I'm probably telling you things you already know. This is not only to respond to you but also me laying out the situation around the "stackless" design for anyone else reading this. Sorry for the lengthy response! You're correct about the blurb, the whole system is really not designed to nest Also, look at it this way, if you ran Lua code in a nested Not everybody cares about this ofc, and I get that, but that's the niche that I've decided to fit In your case, one possibility you already figured out, which is to collect the query into a big Another approach you may not have considered is to follow the Lua iterator protocol. You return an iterator function and any arbitrary control value, and the iterator function is called to update the control value until the control value becomes nil. This would allow Lua to use generic for loops to iterate over the results. This is very similar to the approach you already stated with storing an iterator, but like you said.. you do have to deal with the lifetimes of an iterator, and may need to use something other than a plain Rust iterator to store the Lua iterator state. You would probably have run into a similar problem no matter the approach, because passing references to items of that Rust Iterator still has the same lifetime problems, though you could theoretically have gotten around that with the You can still take your original desired approach of calling Lua functions for each element of the query result, but what you can't do is get around the lifetime limitations of this. Using a nested The system you're wanting only works if the only way to exit out of / suspend the inner loop of Lua is through unwinding through the Rust stack frame holding the iterator, and yeah.. that goes against the way I think the best advice I can give right now is to try as little as possible to fight with Lua about data with non-'static Rust lifetimes. Lua is not designed to deal with non-'static data, you can smuggle references to anything to almost anywhere from almost anywhere, it's just how the language works. This means that most of the time, you want to make the data you pass to Lua 'static and just live with this limitation. In your case of a World query, you could make the case that returning a collected query as a Lua iterator is maybe a potential abuse vector, since the script can make large queries and then never actually iterate them, holding arbitrary amounts of memory. You can either record this memory through I know this is all very very complicated, and what we need is a library of combinators to handle each of these common situations so the user can just choose which tradeoff they want. I want to push back though on the idea that these problems are unique to
|
Thanks for the reply! I actually dug through discord shortly after posting and found more or less this exact spiel from you to more or less the exact question 😁 Anyway, agreed that it's good to have it here as well for visibility 👍
Yep, this is precisely what I'm doing now! And that's a very good point regarding the callback vs table access overhead that I hadn't considered, but makes a lot of sense now that you mention it.
I considered this as well, but as you mentioned - lifetimes are still problematic. Once I switched over to collecting into a One other vague idea I considered trying out would be to yield some sort of "Please create this query" result all the way up to Rust, create and freeze the query, and then resume Lua execution with that. It would invalidate the |
I am truly sorry that you had to listen to it twice 😆
Absolutely it's possible, I didn't even bother to implement the "main thread" distinction that PUC-Rio Lua has with yieldable / un-yieldable threads.
|
I suspect I'm running up against the downside of the "stackless" style here.
In Rust, I have a reference to some
World
that I'm wrapping in apiccolo_util::Frozen
and sticking in a global Lua variable to be accessed from Lua. This works great forWorld
methods that return static things. When doing more complex queries on the world though, I end up with an iterator which contains a reference to theWorld
, which thanks toFrozen
, is only valid within the call toFrozen::with
.My first approach was to pass a
Function
from Lua to Rust which takes the iterator'sItem
, and create anExecutor
to run it inside the callback. Then I found this blurb in theExecutor
docs:I'm guessing these "normal mechanisms" are
CallbackReturn
s likeCall
andSequence
? Since neither the World reference nor the iterator can escape theFrozen::with
callback, it doesn't seem like I'd be able to wrap and return the iterator directly, and it can't be recreated and resumed from an arbitrary point. And even if that were possible, it would be handing out references which themselves couldn't beFunction::bind
'ed and escape.It seems like the best approach available to me is to copy/collect the iteration results into a
Table
and return that back to Lua, but I'd like to avoid that overhead if possible.Is there anything I've missed? Any other approaches to consider?
The text was updated successfully, but these errors were encountered: