我试图在Rust中构建一个简单的UI,但是在Lua中部分编写脚本,使用rust-lua53,并且在设置Lua组件访问Lua状态的好方法时遇到了问题。这个问题/例子比我想要的长一点,抱歉!
UI的核心是Widget
特征,其中包含按下按键时或在重绘屏幕/窗口时调用的方法。类型参数C
是我想要传递的上下文(见后面)。
trait Widget<C> {
fn handle_key<'c>(&mut self, key: char, context: &'c mut C);
}
有一个UI
结构,它处理事件循环,读取键并调用draw
方法(问题遗漏)。 Widget
特征和UI
亚军是通用的,可以在没有与Lua相关的任何内容的情况下使用。
struct UI {}
impl UI {
pub fn run<'c, 'm, T, C>(&mut self, widget: 'm T, context: &'c mut C)
where C: 'c, T: Widget<C>
{
}
}
您可以通过实施Widget
并调用ui.run(widget)
来使用它,该struct RL<'a> {
marker: PhantomData<(&'a ())>,
}
impl<'a> RL<'a> {
pub fn get<T>(&mut self) -> Option<Ptr<T>>
where T: Any
{ unimplemented!() }
pub fn register(&mut self, func: (&'static str, fn(&mut RL) -> ()))
{ unimplemented!() }
}
运行一个事件循环,直到它完成&#34; (比如在窗口小部件上按下一个按钮),然后控制权返回给调用者。
Lua状态的包装器,其中包括安全地获取指向Rust对象的指针:
Rc<RefCell<T>>
有一个智能指针(它只是一个struct Ptr<T> {
obj: Rc<RefCell<T>>,
}
impl<T> Clone for Ptr<T> {
fn clone(&self) -> Self {
Ptr{ obj: self.obj.clone() }
}
}
impl<T> Ptr<T> {
pub fn borrow_mut<'a>(&'a mut self) -> RefMut<'a, T> where T:'a {
(*self.obj).borrow_mut()
}
}
)与传递给Lua的对象一起使用,因此Rust代码可以执行可变的操作,即使在Lua状态中存储了一个引用:
MyWidget
最后有MyWidget
,它应该是允许Lua代码实现小部件的垫片,这就是我目前的困难所在。我们的想法是:
MyWidget
确实需要(可变)访问Lua状态,例如能够调用Lua回调。&mut
由于一般UI::run
别名规则(显然在许多其他地方使用过),因此无法存储对Lua状态的可变引用。Widget
并传递给C
方法(因此在上面添加struct MyWidget {}
struct MyContext<'a> {
rl: &'a mut RL, // mutable reference to the Lua state
}
impl<'b> Widget<MyContext<'b>> for MyWidget {
fn handle_key(&mut self, key: char, context: &mut MyContext) {
unimplemented!()
}
}
impl MyWidget {
// This static method is called from Lua, where `MyWidget` has been made available as a userdata.
pub fn l_run(rl: &mut RL) {
// First get a Rust pointer to the widget out of the Lua state
let mut ui: Ptr<MyWidget> = rl.get().unwrap();
// Create a fresh UI runner
let mut rui = UI{};
// Make the context including the Lua state
let mut ctxt: MyContext = MyContext { rl: rl, };
// Run the widget, passing the context.
rui.run(&mut *ui.borrow_mut(), &mut ctxt);
}
}
参数)。fn main() {
let mut rl = RL{marker: PhantomData};
rl.register(("l_run", MyWidget::l_run));
}
最后,需要注册l_run方法:
error: cannot infer an appropriate lifetime due to conflicting requirements [E0495]
--> <anon>:57:35
|>
57 |> let mut ctxt: MyContext = MyContext { rl: rl, };
|> ^^^^^^^^^
help: consider using an explicit lifetime parameter as shown: fn l_run<'a>(rl: &'a mut RL<'a>)
--> <anon>:53:5
|>
53 |> pub fn l_run(rl: & mut RL) {
|> ^
目前的尝试导致:
error: mismatched types [--explain E0308]
--> <anon>:74:27
|>
74 |> rl.register(("l_run", MyWidget::l_run));
|> ^^^^^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter
note: expected type `fn(&mut RL<'_>)`
note: found type `fn(&'r mut RL<'r>) {MyWidget::l_run}`
note: expected concrete lifetime is lifetime ReSkolemized(0, BrAnon(0))
但是,如果我接受编译器的建议并添加显式生命周期参数,则该函数不再与注册时所需的签名相匹配,而是获取:
<script type="text/javascript">
var x = document.getElementById("demo");
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition, showError);
} else {
x.innerHTML = "Geolocation is not supported by this browser.";
}
}
function showPosition(position) {
x.innerHTML = "Latitude: " + position.coords.latitude +
"<br>Longitude: " + position.coords.longitude;
}
function showError(error) {
switch(error.code) {
case error.PERMISSION_DENIED:
x.innerHTML = "User denied the request for Geolocation."
break;
case error.POSITION_UNAVAILABLE:
x.innerHTML = "Location information is unavailable."
break;
case error.TIMEOUT:
x.innerHTML = "The request to get user location timed out."
break;
case error.UNKNOWN_ERROR:
x.innerHTML = "An unknown error occurred."
break;
}
}
navigator.geolocation.getCurrentPosition(success, error);
function success(position) {
var GEOCODING = 'https://maps.googleapis.com/maps/api/geocode/json?latlng=' + position.coords.latitude + '%2C' + position.coords.longitude + '&language=en';
$.getJSON(GEOCODING).done(function(location) {
$('#latitude').html(position.coords.latitude);
$('#longitude').html(position.coords.longitude);
$.ajax({
url:'table.php',
method: 'POST',
dataType: 'json',
data:{
lat: $('#latitude').val(),
lng: $('#longitude').val()
},
success: function(data){
console.log();
}
});
}
function error(err) {
console.log(err)
}
</script>
因此修复上一个错误意味着签名不再与注册函数兼容(这不是通用的;实际上我一次性传递具有多个函数的切片)。
答案 0 :(得分:2)
这些深刻的终身问题很多,所以让我们看看我们是否可以解决这个问题。让我们先来看一下具有相同错误的代码的精简版本:
struct RunLoop<'a> {
marker: &'a u8,
}
struct MyContext<'a> {
rl: &'a mut RunLoop<'a>,
}
fn run(rl: &mut RunLoop) {
let mut ctxt = MyContext { rl: rl };
}
fn main() {}
MyContext
的定义指出需要提供对RunLoop
的引用。 RunLoop
的生命周期和参数化RunLoop
的生命周期需要统一 - 它们都设置为'a
。但是,根据run
的签名无法保证这一点。众所周知的是,有两个生命周期,现在都被省略了。
这导致了一个解决方案:我们可以明确地识别两个生命周期并建立它们之间的关系:
struct MyContext<'a, 'b : 'a> {
rl: &'a mut RunLoop<'b>,
}
另一个解决方案是编译器暗示的解决方案:预先统一调用run
时的生命周期:
fn run<'a>(rl: &'a mut RunLoop<'a>) {
然而,后一种解决方案在较大的程序中不起作用,但却失败了:
error: mismatched types [--explain E0308]
--> src/main.rs:74:27
74 |> rl.register(("l_run", MyWidget::l_run));
|> ^^^^^^^^^^^^^^^ expected concrete lifetime, found bound lifetime parameter
note: expected type `fn(&mut RL<'_>)`
note: found type `fn(&'a mut RL<'a>) {MyWidget::l_run}`
note: expected concrete lifetime is lifetime ReSkolemized(0, BrAnon(0))
(附注:由于我在错误消息中看到ReSkolemized
提及,所以很长时间了!)
让我们扩展我们的小例子以生成相同的错误:
struct RunLoop<'a> {
marker: &'a u8,
}
struct MyContext<'a> {
rl: &'a mut RunLoop<'a>,
}
fn run<'a>(rl: &'a mut RunLoop<'a>) {
let mut ctxt = MyContext { rl: rl };
}
fn register(func: fn(&mut RunLoop)) {}
fn main() {
register(run);
}
我不太确定。我知道在引用上放几乎任何显式生命周期都有助于编译:
fn register<'a>(func: fn(&'a mut RunLoop<'a>)) {}
fn register<'a, 'b>(func: fn(&'a mut RunLoop<'b>)) {}
fn register(func: fn(&'static mut RunLoop)) {}