I have an Emitter that emits Watchers. The Watcher is RefCounted. When it is dropped, they are expected to untangle and remove themselves from the emitter, through a Drop trait. It's not working because the Emitter seems to be holding on to a reference to it. As far as I can tell, this only happens when the Emitter has a signal connection to the Watcher. Without the signal, this doesn't happen.
This does not match a GDScript equivalent. When a the last RefCounted reference is lost (or a Node is freed, etc), a signal doesn't hold it open. I have some sample code of what this looks like.
use godot::prelude::*;
#[derive(GodotClass)]
#[class(base=RefCounted,no_init)]
struct DropEmitter {
#[export]
label: GString,
base: Base<RefCounted>,
}
#[godot_api]
impl IRefCounted for DropEmitter {}
#[godot_api]
impl DropEmitter {
#[func]
fn make_with_label(label: GString) -> Gd<Self> {
Gd::from_init_fn(|base| Self { label, base })
}
#[signal]
fn trigger_watchers();
#[func]
fn emit_watcher(&mut self, watcher: GString) -> Gd<DropWatcher> {
let watch_label = format!("{}:{}", self.label, watcher);
let t = DropWatcher::make_with_label(GString::from(&watch_label));
self.signals()
.trigger_watchers()
.connect_other(&t, DropWatcher::emit_test_signal);
t
}
}
#[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);
}
}
GDScript Testing
extends Node
class Emitter:
signal trigger
func make_watcher(label:String)->DropWatcher:
var output = DropWatcher.make_with_label(label)
trigger.connect(output.emit_test_signal)
return output
var drop_emitter = DropEmitter.make_with_label("test emitter")
func _ready() -> void:
print("==== test: drop watcher_inside ====")
var watcher_inside := drop_emitter.emit_watcher("watcher inside emitter")
watcher_inside = null
print("==== test: drop pure_watcher ====")
var pure_watcher := DropWatcher.make_with_label("pure watcher")
pure_watcher = null
print("==== test: drop side_watcher ====")
var side_watcher := DropWatcher.make_with_label("side attached watcher")
drop_emitter.trigger_watchers.connect(side_watcher.emit_test_signal)
side_watcher = null
print("==== test: drop emitter ====")
drop_emitter = null
print("==== test: try gd script version ====`")
var emitter = Emitter.new()
var gd_script_watcher = emitter.make_watcher("gd script watcher")
print("==== test: drop gd watcher ====`")
gd_script_watcher = null
print("==== test: drop gd emitter ====`")
emitter = null
pass
I have an
Emitterthat emitsWatchers. TheWatcherisRefCounted. When it is dropped, they are expected to untangle and remove themselves from the emitter, through a Drop trait. It's not working because theEmitterseems to be holding on to a reference to it. As far as I can tell, this only happens when theEmitterhas a signal connection to theWatcher. Without the signal, this doesn't happen.This does not match a GDScript equivalent. When a the last RefCounted reference is lost (or a Node is freed, etc), a signal doesn't hold it open. I have some sample code of what this looks like.
GDScript Testing