Signal parameters of object type are generated as Gd<T>, which is non-nullable. But Godot passes null for some of them, so
connecting a handler to such a signal can panic and isn't easily avoidable by the user.
The clearest case is EditorPlugin::scene_changed, whose scene_root is null when the last scene tab is closed:
#[godot_api]
impl IEditorPlugin for MyPlugin {
fn enter_tree(&mut self) {
self.signals()
.scene_changed()
.connect(|scene_root: Gd<Node>| {
// Never runs -- closing the last scene tab passes null, and conversion panics before we get here.
godot_print!("now editing {scene_root}");
});
}
}
EditorPlugin::resource_saved (resource: Resource) has the same problem. There are likely more.
Some ideas:
-
Use Option<Gd<T>> everywhere.
- Technically correct, but requires
unwrap() even if object parameters are guaranteed non-null.
-
Keep Gd<T>, add a *_nullable overload with Option<Gd<T>>, deprecating the original.
- Can be added incrementally, as users report problems.
- Increases API surface, deprecation churn.
- Note: nullability is per-object, not per-signal. Needs decision on what to do for signals with multiple object parameters.
-
Same signal uses either Gd or Option<Gd> (exclusive).
- Hand-maintained list of affected
(class, signal, parameter) triples in godot-codegen/src/special_cases.rs (but also true for other approaches).
- Needs ahead-of-time work to find out which are nullable and which not; hard to correct later without minor version bump.
Signal parameters of object type are generated as
Gd<T>, which is non-nullable. But Godot passesnullfor some of them, soconnecting a handler to such a signal can panic and isn't easily avoidable by the user.
The clearest case is
EditorPlugin::scene_changed, whosescene_rootis null when the last scene tab is closed:EditorPlugin::resource_saved(resource: Resource) has the same problem. There are likely more.Some ideas:
Use
Option<Gd<T>>everywhere.unwrap()even if object parameters are guaranteed non-null.Keep
Gd<T>, add a*_nullableoverload withOption<Gd<T>>, deprecating the original.Same signal uses either
GdorOption<Gd>(exclusive).(class, signal, parameter)triples ingodot-codegen/src/special_cases.rs(but also true for other approaches).