Skip to content

Instantly share code, notes, and snippets.

@rust-play
Created August 20, 2019 01:01
Show Gist options
  • Save rust-play/d16aa850a8944fb01f170fc344cfc129 to your computer and use it in GitHub Desktop.
Save rust-play/d16aa850a8944fb01f170fc344cfc129 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
#![allow(dead_code)]
// Suppose we have a shared context for a lot of values, in this specific case,
// this is a shared lua_State for lots of values which live in the lua registry.
struct Lua;
// This is a representation of a value that lives in that shared context. In
// the real api, this is an enum that can represent any lua value, and most
// instances of it are handles to something that points to the lua registry,
// so logically it would hold a reference to the shared Lua state.
struct LuaValue<'a>(&'a Lua);
// These are traits for types that are convertible to and from LuaValue. The
// traits *must* have a lifetime parameter, because some convertible types
// themselves reference the lua context.
trait ToLua<'a> {
fn to_lua(self, &'a Lua) -> LuaValue<'a>;
}
trait FromLua<'a> {
fn from_lua(&'a Lua, LuaValue<'a>) -> Self;
}
// Let's make an example type that is convertible to / from lua, and also
// references the lua context. This is obviously a faked example, but this
// type actually exists in the real api.
struct LuaTable<'a>(&'a Lua);
impl<'a> ToLua<'a> for LuaTable<'a> {
fn to_lua(self, lua: &'a Lua) -> LuaValue<'a> {
LuaValue(lua)
}
}
impl<'a> FromLua<'a> for LuaTable<'a> {
fn from_lua(lua: &'a Lua, _: LuaValue<'a>) -> Self {
LuaTable(lua)
}
}
// Now, suppose we also want to be able to talk about functions from a LuaValue
// to another LuaValue, that could be used by lua.
type LuaCallback = Box<dyn for<'a> Fn(&'a Lua, LuaValue<'a>) -> LuaValue<'a>>;
// It would be nice to be able to take a function that takes any type
// that could be converted from lua, and returns any type that can be converted
// to lua, and make that into a callback, but this is where the trouble starts!
// This does not borrow check, because the 'a parameter here cannot reference a
// lifetime that exists at the time this function is called, it needs to be
// for<'a>
/*
fn wrap_callback<'a, A, R, F>(mut f: F) -> LuaCallback
where A: FromLua<'a>,
R: ToLua<'a>,
F: 'static + FnMut(&'a Lua, A) -> R
{
Box::new(move |lua, a| {
f(lua, A::from_lua(lua, a)).to_lua(lua)
})
}
*/
// So let's try that instead. Hmmm, this is SORT of right, but there are three
// different 'as here.
fn wrap_callback<A, R, F>(f: F) -> LuaCallback
where A: for<'a> FromLua<'a>,
R: for<'a> ToLua<'a>,
F: 'static + for<'a> Fn(&'a Lua, A) -> R
{
Box::new(move |lua, a| {
f(lua, FromLua::from_lua(lua, a)).to_lua(lua)
})
}
// Let's make an example function and try to wrap it
fn example_function<'a>(_lua: &'a Lua, arg: LuaTable<'a>) -> LuaTable<'a> {
arg
}
// Drat, this does not work, and gives a very confusing lifetime error
/*
fn test_wrapper() -> LuaCallback {
wrap_callback(example_function)
}
*/
// Well, let's see if we got the type signature of wrap_callback wrong. This
// works, so certainly the idea is sound, we've just gotten the type signature
// of wrap_callback wrong somehow
fn test_manual_wrap() -> LuaCallback {
Box::new(|lua, a| {
example_function(lua, FromLua::from_lua(lua, a)).to_lua(lua)
})
}
// Hmm, can't seem to figure out any type signature that will work for
// wrap_callback, it SEEMS like we need some kind of for<'a> that applies to
// multiple trait bounds at once, let's invent a syntax for what we want to
// express, but this doesn't really exist so it doesn't help much
/*
fn wrap_callback<A, R, F>(f: F) -> LuaCallback
where for<'a> (
A: FromLua<'a>,
R: ToLua<'a>,
F: 'static + Fn(&'a Lua, A)
) -> R
{
Box::new(move |lua, a| {
f(lua, FromLua::from_lua(lua, a)).to_lua(lua)
})
}
*/
// Maybe you just can't express the type signature for wrap_callback? Let's try
// and make a function that does the same thing using a closure
/*
fn test_closure_wrap() -> LuaCallback {
let wrapper = |f| {
Box::new(move |lua, a| {
f(lua, FromLua::from_lua(lua, a)).to_lua(lua)
})
};
wrapper(example_function)
}
*/
// That doesn't work either! We seem to have discovered an expression that if
// typed directly, will work, but if you place that expression in a closure
// or free function, does not borrow check!
// Is this just currently impossible in rust?
fn main() {}
@towry
Copy link

towry commented Aug 20, 2019

mark

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment