gdnative-book
gdnative-book copied to clipboard
Deciding between `&T` and `TRef<T>`
In general godot-rust allows to use e.g. either owner: &Node or owner: TRef<Node> on exported methods, as noted in the Class registration section:
The parameter can be a shared reference &T or a TRef<T>.
Unless I have missed something, the book doesn't go into details why one would chose one over the other. In practice this means that most developers go for &T because it's simpler. This is probably the reason why many people run into problems when trying to set up signals, in particular because this example suggest that passing owner should just work. Searching the discord history shows many cases of failed attempts to get signal connection to work, because owner.connect takes a target: AsArg which only works with owner: TRef<T> but not with owner: &T (unless falling back to unsafe { owner.assume_shared() }). I'm not sure if this limitation is by design or can perhaps be avoided eventually (c.f. https://github.com/godot-rust/godot-rust/issues/749). So far this is the only difference I'm aware of, but there may be further differences. Regarding the book I'd suggest:
- In case one form is a strict superset of the other, the book should probably recommend using only the more powerful one. I.e, if the
AsArglimitation is the only difference, it would make sense to recommend usingTRef<T>because it can do strictly more than&T. - If there are things that can only be done by one form, but not by the other and vice versa, it would be nice to list these pros/cons of both forms to help with the decision.
Thanks for input! I think there's merit to adding information in the Ref, TRef, and Instance section of the book.
Would you like to take point on a PR for mentioning this?
Would you like to take point on a PR for mentioning this?
If you mean if I could contribute anything: I guess I'm still struggling too much with some fundamental questions like:
- Is
AsArgthe only real difference between&TandTRef<T>? - Are there other practical use cases despite setting up signals that require
AsArg? - Since
AsArgseems limiting, why is it needed at all, and could&Tsupport it as well?
Especially the latter needs to be checked first from the bindings perspective. CC @Bromeon
As as side note, answering (2) is often a bit tricky, presumably due to the auto-generated nature of the bindings. For instance when searching the docs for AsArg it shows zero usages "In Parameters" and "In Return Types", making it hard to understand its purpose from its usage:

Is
AsArgthe only real difference between&TandTRef<T>?
In practice, it's mostly that and claim(), the ability to turn the TRef into a Ref, i.e. a persistent reference you can store for later use.
You can imagine a TRef<T> as a bare &T shared reference + some book-keeping overhead to integrate it with GDNative.
Since
AsArgseems limiting, why is it needed at all, and could&Tsupport it as well?
A lot of this functionality is part of GodotObject, which is the bound for T in TRef<T>, and not part of TRef itself.
A TRef contains no other data than &T:
pub struct TRef<'a, T: GodotObject, Own: Ownership = Shared> {
obj: &'a T,
_marker: PhantomData<Own>,
}
However, it contains other information, namely the generic type parameter Own. Among other things, the ownership policy determines which TRef instantiations are safe to be passed to the library (e.g. ThreadLocal ones are not).
So no, we cannot safely implement AsArg for &T at the moment.
This being said, a lot of these things are currently in the process of being re-thought, see https://github.com/godot-rust/godot-rust/issues/808 for example. So while we can give recommendation on when to use TRef<T> and when to use &T, this may become obsolete.
Generally, with TRef<T> you're on the safe side, as you can always use the Deref impl to turn it into a &T. There's no real reason to use &T except for simplicity/ergonomics.
Thanks for the clarifications! In this case it might be best to wait a bit and see how things evolve.