- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试在 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
runner 是通用的,可以在没有任何 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)
,它会运行一个事件循环,直到它“完成”(比如在小部件上按下一个按钮),然后控制权返回给调用者。
Lua 状态有一个包装器,它(除其他外)处理安全地获取指向 Rust 对象的指针:
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!() }
}
有一个智能指针(它只是一个 Rc<RefCell<T>>
)与传递给 Lua 的对象一起使用,因此即使在 Lua 状态中隐藏了一个引用,Rust 代码也可以做可变的事情:
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()
}
}
最后是 MyWidget
,这应该是一个让Lua代码实现widgets的shim,而这正是我目前的难点所在。思路是:
MyWidget
确实需要(可变)访问 Lua 状态,例如能够调用 Lua 回调。MyWidget
由于一般 &mut
,无法存储对 Lua 状态的可变引用别名规则(它显然在许多其他地方使用)。UI::run
然后转到 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);
}
}
最后,需要注册 l_run 方法:
fn main() {
let mut rl = RL{marker: PhantomData};
rl.register(("l_run", MyWidget::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))
因此修复之前的错误意味着签名不再与注册函数兼容(这不是通用的;实际上我一次性传入了包含多个函数的切片)。
最佳答案
这些深层次的生命周期问题很棘手,所以让我们看看能否解决。让我们先看看具有相同错误的代码的精简版:
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)) {}
关于rust - 将可变上下文传递给回调,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39089905/
为什么禁用类型像 type t = A of int | B of string * mutable int 虽然允许此类类型: type t = A of int | B of string * i
我正在寻找一种类似结构的数据结构,我可以从中创建多个实例并具有某种类型提示而不是不可变的。 所以我有这样的东西: class ConnectionConfig(NamedTuple): nam
我需要转到引用的结构: class SearchKnot { var isWord : Bool = false var text : String = "" var to
如sec 10.4.3中所述 当控制进入执行时,执行以下步骤 功能对象F(调用者)中包含的功能代码的上下文 提供thisArg,而调用方提供argumentsList: 如
i make a game that start display Activity indicator And activity indicator bottom display UiLable wi
编辑:我在这里不断获得支持。只是为了记录,我认为这不再重要。自从我发布它以来我就不再需要它了。 我想在 Scala 中执行以下操作... def save(srcPath: String, destP
使用可变对象作为 Hashmap 键是一种不好的做法吗?当您尝试使用已修改足以更改其哈希码的键从 HashMap 中检索值时,会发生什么? 例如,给定 class Key { int a; /
如果您在Kotlin中访问List类型的Java值,则将获得(Mutable)List!类型。 例如。: Java代码: public class Example { public stati
我编写了 str 类(内置)的以下扩展,以便执行以下操作:假设我有字符串 "Ciao" ,通过做"Ciao" - "a"我想要的结果是字符串 "Cio" 。这是执行此操作的代码,并且运行良好: cla
使用可变对象作为 Hashmap 键是一种不好的做法吗?当您尝试使用已修改足以更改其哈希码的键从 HashMap 中检索值时,会发生什么? 例如,给定 class Key { int a; /
我正在为我的公司设计一个数据库来管理商业贷款。每笔贷款都可以有担保人,可以是个人或公司,在借款业务失败时作为财务支持。 我有 3 个表:Loan、Person 和 Company,它们存储明显的信息。
我使用二进制序列化从 C# 类中保存 F# 记录。一切正常: F#: type GameState = { LevelStatus : LevelStatus
import javax.swing.JOptionPane; public class HW { public static void main(String[] args) { Strin
使用 flatbuffer mutable 有多少性能损失? 是否“正确”使用 FlatBuffers 来拥有一个应该可编辑的对象/结构(即游戏状态) 在我的示例中,我现在有以下类: class Ga
std::function create_function (args...) { int x = initial_value (args...); return [x] () mut
我需要在 for 循环中找到用户输入的字符。我通常会这样做 如果(句子[i] == 'e') 但是因为在这里,'e' 将是一个单字母字符变量,我不知道如何获取要比较的值。我不能只输入 if (sent
我有一个这样的算法: let seed: Foo = ... let mut stack: Vec = Vec::new(); stack.push(&seed); while let Some(ne
这个问题可能看起来非常基础,但我很难弄清楚如何做。我有一个整数,我需要使用 for 循环来循环整数次。 首先,我尝试了—— fn main() { let number = 10; // An
如果我有以下结构: struct MyStruct { tuple: (i32, i32) }; 以及以下函数: // This will not compile fn function(&mut s
我希望在每个 session 的基础上指定列的默认值。下面的脚本不起作用,但描述了我想如何使用它。我目前使用的是 MySQL 5.5.28,但如果需要可以升级。 CREATE TABLE my_tbl
我是一名优秀的程序员,十分优秀!