将可变上下文传递到回调中

2024-03-15

我正在尝试用 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特质和UIrunner 是通用的,可以在没有任何 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代码实现小部件,这就是我当前的困难所在。想法是:

  • MyWidget确实需要(可变)访问 Lua 状态,例如能够调用 Lua 回调。
  • MyWidget由于一般原因,无法存储对 Lua 状态的可变引用&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))

(旁注:这是一个long自从我看到一个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)) {}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

将可变上下文传递到回调中 的相关文章

随机推荐

  • 高分表

    我希望在我的 Android 应用程序中添加一个 本地 而不是在线 高分表 并且我想了解解决该问题的最佳方法 我有一个用户列表 现在被保存到文件中并作为 User 对象数组读回 高分需要引用此数据以使用用户的姓名和照片等填充表 对于显示器
  • Google Chart Gannt 避免工具提示

    通过研究 我发现 Gannt Google Chart 上显示的工具提示不可自定义 因此我决定覆盖它 捕获由我的 gannt 矩形触发的悬停事件 如下所示 google visualization events addListener ch
  • 如何触发 IModel.BasicAcks?

    我第一次使用 RabbitMQ 的 NET API 我想出了一个对我来说似乎合理的用例 我想创建发布消息并在消息被确认后执行某些操作的发布者 IModel BasicAcks 事件似乎是了解这一点的好方法 所以 我给出版商写了一封信 pri
  • 识别并计算咒语(每组内的独特事件)

    我正在寻找一种有效的方法来识别时间序列中的咒语 运行 在下图中 前三列是我所拥有的 第四列 spell这就是我想要计算的 我尝试过使用dplyr s lead and lag 但这变得太复杂了 我试过了rle但一无所获 ReprEx df
  • 如何在android中检查3g是否活跃

    我正在尝试检查我的手机中的 3G 是否处于活动状态 之后我必须触发一个 Intent 所以请有人帮助我 提前致谢 我最近编写的应用程序的另一个片段 TelephonyManager telManager telManager Telepho
  • 如何在命令类之外获取命令参数?

    我向doctrine fixtures load 命令添加了自定义选项 现在我想知道如何在自定义装置类中获取此命令选项 class LoadUserData implements FixtureInterface ContainerAwar
  • Matlab 中多个字符串的日期

    我必须从两个字符串生成一个日期 字符串或数字 第一个是日期 第二个是时间 我一定在代码中犯了一些错误 因为结果与源数据的串联不同 DIR4 h datestr strcat DIR1 h DIR2 h dd mm yyyy HH MM SS
  • Django 403 CSRF 令牌丢失或不正确

    我遇到过这个问题 但不幸的是仍然不知道如何解决它 表单呈现完美 我输入信息并收到 CSRF 错误 给出的理由是token missing or incorrect View def eventSell request id c c upda
  • 循环 this.props.children 如何测试它们的类型?

    在自定义 React 组件中的以下代码片段中 React Children map this props children child gt if predicate child do stuff else do other stuff p
  • 简单的网络服务器或网络测试框架

    需要测试一个复杂的 Web 应用程序 该应用程序与基于远程第 3 方 CGI 的 Web 服务进行交互 我计划在虚拟网络服务器中实现一些第三方服务 以便我可以完全控制测试用例 寻找一个简单的 python http web 服务器或框架来模
  • C 结构中的填充量是编译器相关的还是明确定义的?

    是C中的填充量struct在标准或编译器和 或目标体系结构中明确定义 我在标准中找不到答案 它是实现定义的 来自 C99 标准第 6 7 2 1 节 结构体或联合对象的每个非位域成员都按照实现定义的方式对齐 适合其类型的方式 可能还有无名的
  • 使用VBA代码从tr获取所有td

    我有一个 tr 它是一个对象并且有 td 我想获取另一个对象变量 tblTD 中的所有 td 为此我用了Set tblTD tr getelementsbytagname td 但是当我检查 tblTD 的长度时 它显示为 0 有人可以建议
  • C# WPF Webbrowser msHTML - 探索 DOM - 查找元素

    我实际上正在使用 WPF 和 WPF WebBrowser 用 C 开发一个个人项目 我真的需要探索 html DOM 元素 就像我们过去在 javascript 或 php 中所做的那样 在我的主窗口中我有这个变量 private msh
  • 将 2D 箱中分散值的平均值绘制为直方图/十六进制图

    我有 3 维分散数据 x y z 我想将 x 和 y 的箱中 z 的平均值绘制为十六进制图或二维直方图 有没有 matplotlib 函数可以做到这一点 尽管这似乎是一个常见问题 但我只能提出一些非常麻烦的实现 例如 像这样的东西 除了颜色
  • #if canImport(CoreImage) 在 swift 包管理器中不起作用

    我制作使用 CoreImage 的库 库支持 Cocoapods Carthage 和 Swift 包管理器 当我在 Swift 包管理器中构建它时 我收到此错误 Undefined symbols for architecture x86
  • 空 ByteArrayOutputStream / ZipOutputStream 的长度 = 22?

    我有一个关于流大小的小问题 这是我的尝试 ByteArrayOutputStream outStream new ByteArrayOutputStream ZipOutputStream zipStream new ZipOutputSt
  • 从 SQL Server 导出到带有列标题的 Excel?

    我有一个大约有 20 列的查询 我想将其导出到带有列标题的 Excel 文件 我以为这很容易弄清楚 但运气不好 我在网上搜索并发现了一项建议 但最终没有奏效 所以我陷入了困境 工具 gt 选项 选择 查询结果 gt SQL Server g
  • Angular 2.0 和 ng 风格

    我正在构建一个 Angular 2 0 组件 我想动态控制它的样式 使用ng style 快速查看 Angular 2 的文档后 我尝试了以下操作 div class theme preview fontSize div 并看到尺寸实际上打
  • WPF 切角元素

    我正在尝试在 WPF 中创建类似于下图的内容 该控件被设计为我的应用程序中所有内容的基本视图 并将位于带有背景 可能是某种渐变 的 Window 控件内 要求如下 三边圆角 左上 左下 右下 剪掉右上角的选项卡查看角 剪切区域 后面的背景透
  • 将可变上下文传递到回调中

    我正在尝试用 Rust 构建一个简单的 UI 但部分可以在 Lua 中编写脚本 使用 rust lua53 并且在找到一种让 Lua 组件访问 Lua 状态的好方法时遇到问题 这个问题 示例比我想要的要长一点 抱歉 UI 的核心是Widge