rust - 将可变实例方法传递给函数
问题描述
我通过为自己创建一个玩具 SDL2 库来自学 Rust。
我在 Go 中创建了类似的东西,并试图移植我的代码。到目前为止,这是我无法克服的问题。我希望我的库对程序状态的函数进行回调,以便我可以将键盘事件从我的库代码发送到我的客户端代码。
目的是让来自 SDL 键盘事件泵的 keydown 事件触发状态对象上的 on_keydown 函数。如果我删除 State 结构并只使用静态函数,那么它就可以工作。当然,这会阻止我根据键盘操作更改程序的状态。
我正在尝试尽可能少地使用外部板条箱。
图书馆的相关部分。
pub enum GameCommand {
Quit,
Continue,
}
pub struct Window {
keydown_event: fn(Event) -> GameCommand,
}
impl Window {
pub fn set_keydown_event(&mut self, f: fn(e: Event) -> GameCommand) {
self.keydown_event = f;
}
pub fn run(&mut self) -> Result<(), String> {
let mut event_pump = self.game.context.event_pump()?;
'running: loop {
// Handle events
for event in event_pump.poll_iter() {
let mut gc = GameCommand::Continue;
match event {
Event::Quit { .. } => break 'running,
Event::KeyDown { repeat: false, .. } => {
gc = (self.keydown_event)(event);
}
_ => {}
}
if let GameCommand::Quit = gc {
break 'running
}
}
}
Ok(())
}
}
现在是客户端bin的相关部分。
struct State {
bgcolor: Color,
}
impl State {
fn on_keydown(&mut self, event: Event) -> GameCommand {
match event {
Event::KeyDown { keycode: Some(Keycode::R), .. } => {
self.bgcolor.r += 1;
GameCommand::Continue
},
Event::KeyDown { keycode: Some(Keycode::G), .. } => {
self.bgcolor.g += 1;
GameCommand::Continue
},
Event::KeyDown { keycode: Some(Keycode::B), .. } => {
self.bgcolor.b += 1;
GameCommand::Continue
},
Event::KeyDown { keycode: Some(Keycode::Escape), ..} => {
GameCommand::Quit
},
_ => GameCommand::Continue,
}
}
}
现在主要功能。
fn main() -> Result<(), String> {
let mut state = State {
bgcolor: Color::RGB(0, 0, 0),
};
let mut window = Window::new();
window.set_keydown_event(state.on_keydown);
Ok(())
}
有很多代码被跳过以保持简短。我用这段代码得到的错误是。
{
"code": "E0615",
"message": "attempted to take value of method `on_keydown` on type `State`\n\nmethod, not a field\n\nhelp: use parentheses to call the method: `(_)`",
}
如果我window.set_keydown_event(state.on_keydown);
收到此错误。
{
"code": "E0308",
"message": "mismatched types\n\nexpected fn pointer, found enum `sdlgame::GameCommand`\n\nnote: expected fn pointer `fn(sdl2::event::Event) -> sdlgame::GameCommand`\n found enum `sdlgame::GameCommand`",
}
我认为问题在于函数签名的差异。在它期望的 set_keydown_event 函数中。
fn(Event) -> GameCommand
这就是为什么一个与结构无关的普通函数起作用的原因。对于改变状态的实例方法,它需要签名。
fn on_keydown(&mut self, event: Event) -> GameCommand
最初,我试图实现这是一种单线程方式,因为我试图让事情变得简单,以便我推理出来。多线程将在稍后出现。
这在 Rust 中是否可行,实现此结果的正确方法是什么?
提前致谢。
解决方案
基本上,您需要使用函数特征以及显式闭包,以便将调用绑定到变量。因此,您将更Window
改为使用函数特征:
// F is now the function type
pub struct Window<F: FnMut(Event) -> GameCommand> {
keydown_event: F,
}
然后您将更改您的 impl 以支持该功能特征:
// put generic in impl
impl<F: FnMut(Event) -> GameCommand> Window<F> {
// take F as the parameter type now
pub fn set_keydown_event(&mut self, f: F) {
self.keydown_event = f;
}
pub fn run(&mut self) -> Result<(), String> {
// this function should stay the same
}
}
然后,您将向它传递一个显式闭包:
fn main() -> Result<(), String> {
let mut state = State {
bgcolor: Color::RGB(0, 0, 0),
};
let mut window = Window::new();
// binds the on_keydown function to the state variable
window.set_keydown_event(|x| state.on_keydown(x));
Ok(())
}
推荐阅读
- java - 在 Servlet 类中创建方法是一种不好的做法吗?
- php - Wordpress php if 语句
- javascript - Bootstrap 4.1 导航栏活动类颜色不会改变
- excel - 对象类型内的 VBA 对象类型
- bash - pkill 不是完全杀死进程吗?
- google-api - 来自 Google Proximity API v1beta1 的频繁“503 - 服务不可用”响应
- reactjs - 我可以在不使用 state 或 redux 的情况下在反应路由中获取所有访问的路径吗
- php - mailform contact.php 成功后重新加载页面
- java - 如何使用扫描仪在集合中动态添加数据
- python - Python - 如何从我的 IDE 中调用具有不同命令行参数的脚本?