Rust GTK程序中按键处理器编译失败问题求助
我遇到了一个有点棘手的编译错误,虽然感觉应该是个小问题,但就是找不到症结所在。先贴一下我的代码:
let key_handler = gtk::EventControllerKey::new(); controller.obj().add_controller(&key_handler); key_handler.connect_key_pressed(clone!(@weak controller => move |_ctlr, key, _code, state| { controller.keyboard_input(key, state); gtk::Inhibit(false) }));
这段代码编译时抛出了如下错误:
error[E0069]:
return;in a function whose return type is not()--> src/controller/imp.rs:80:45
|
80 | key_handler.connect_key_pressed(clone!(@weak controller => move |_ctlr, key, _code, state| {
| ________________________________^
81 | | controller.keyboard_input(key, state);
82 | | gtk::Inhibit(false)
83 | | }
84 | | )
| |^ return type is not()= note: this error originates in the macro
clone(in Nightly builds, run with -Z macro-backtrace for more info)For more information about this error, try
rustc --explain E0069.
我怀疑问题出在clone!宏的展开上,但不太确定。有没有简单的修复方法?或者我是不是用错了clone!?就在这段代码上方,我有一段类似的代码却能正常编译:
controller.start_buttonx.connect_clicked(clone!(@weak controller => move |xx| BOARDS[1].emit_by_name::<()>("test-signal", &[&x])));
问题分析
其实这个问题的根源在于clone!宏的行为差异:你上面的connect_clicked闭包返回类型是(),而connect_key_pressed的闭包需要返回gtk::Inhibit类型。
当你用@weak修饰变量时,clone!宏会自动生成代码检查弱引用是否能成功升级为强引用。如果升级失败,宏默认会插入return;语句——这个语句在返回()的闭包里没问题,但在需要返回gtk::Inhibit的闭包里,就会因为返回类型不匹配抛出E0069错误。
而你上面的connect_clicked闭包刚好返回(),所以默认的return;完全符合要求,自然不会报错。
修复方案
你只需要在clone!宏里明确指定弱引用升级失败时的返回值,让它和闭包要求的返回类型一致即可。有两种方式可以实现:
方式一:手动处理弱引用升级
直接不用clone!的自动处理,自己写升级逻辑,更直观:
key_handler.connect_key_pressed(move |_ctlr, key, _code, state| { let Some(controller) = controller.upgrade() else { return gtk::Inhibit(false); }; controller.keyboard_input(key, state); gtk::Inhibit(false) });
方式二:用clone!的@default语法指定返回值
利用clone!宏的扩展语法,指定弱引用失效时的返回行为:
key_handler.connect_key_pressed(clone!(@weak controller => @default return gtk::Inhibit(false); move |_ctlr, key, _code, state| { controller.keyboard_input(key, state); gtk::Inhibit(false) }));
这样修改后,宏生成的代码在弱引用升级失败时会返回gtk::Inhibit(false),和闭包要求的返回类型匹配,编译就能通过了。
备注:内容来源于stack exchange,提问作者russell

