Created
August 20, 2019 01:01
-
-
Save rust-play/d16aa850a8944fb01f170fc344cfc129 to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#![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() {} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
mark