I have an RefCounted called "watcher". It emits a signal. When this signal is called, the watcher is no longer needed, so I null the Watcher object. This causes a full game crash in Rust.
ERROR: Destroyed an object from Godot side, while a bind() or bind_mut() call was active.
This is a bug in your code that may cause UB and logic errors. Make sure that objects are not
destroyed while you still hold a Rust reference to them, or use Gd::free() which is safe.
object: Base { id: -9223372000381106669, class: DropWatcher, refc: 0 }
at: crash (core/core_bind.cpp:349)
GDScript backtrace (most recent call first):
[0] test_nullifier (res://rust_drop_test.tscn::GDScript_u86e1:66)
[1] _ready (res://rust_drop_test.tscn::GDScript_u86e1:51)
GDScript doesn't do this. An object can null itself causing a destroy when the signal is finished. I think in Godot C++, there might be something holding a self reference and that doesn't get release until the call stack is finished? (speculating, haven't read C++ source on it)
A work around with call_deferred does work:
class Watcher extends RefCounted:
signal test_signal
func emit_test_signal():
test_signal.emit()
var rust_watcher = DropWatcher.make_with_label("isolated watcher")
var gd_watcher = Watcher.new()
func _ready() -> void:
print("==== test: GDScript Watcher Nullify")
gd_watcher.test_signal.connect(func():
gd_watcher = null
)
gd_watcher.emit_test_signal()
print("should be null:",gd_watcher)
print("==== test: Rust Watcher Nullify")
rust_watcher.test_signal.connect(func(data):
print("dropping ref")
call_deferred("set","rust_watcher",null)
#rust_watcher = null # <-- commentating this line causes a full game crash
)
print("emitting")
rust_watcher.emit_test_signal()
print("should be null:",rust_watcher)
await get_tree().process_frame
print("should be null:",rust_watcher)
pass
DropWatcher coppied from previous bug, #1665:
#[derive(GodotClass)]
#[class(base=RefCounted,no_init)]
struct DropWatcher {
label: GString,
base: Base<RefCounted>,
}
#[godot_api]
impl IRefCounted for DropWatcher {}
#[godot_api]
impl DropWatcher {
#[func]
fn make_with_label(label: GString) -> Gd<Self> {
Gd::from_init_fn(|base| Self { label, base })
}
#[func]
fn emit_test_signal(&mut self) {
let l = self.label.clone();
self.signals().test_signal().emit(&l);
}
#[signal]
fn test_signal(label: GString);
}
impl Drop for DropWatcher {
fn drop(&mut self) {
godot_print!("{} dropped!", self.label);
}
}
Drop is not strictly necessary, only used here to detect drop event.
I have an RefCounted called "watcher". It emits a signal. When this signal is called, the watcher is no longer needed, so I null the Watcher object. This causes a full game crash in Rust.
GDScript doesn't do this. An object can null itself causing a destroy when the signal is finished. I think in Godot C++, there might be something holding a
selfreference and that doesn't get release until the call stack is finished? (speculating, haven't read C++ source on it)A work around with call_deferred does work:
DropWatchercoppied from previous bug, #1665:Drop is not strictly necessary, only used here to detect drop event.