|
| 1 | +# `Ref`, `TRef` and `Instance` |
| 2 | + |
| 3 | +Objects from Godot, such as scene nodes, materials, or other resources are owned and maintained by the Godot engine. This means that your Rust code will store references to existing objects, not values. godot-rust provides special wrapper types to deal with these references, which are explained in this page. |
| 4 | + |
| 5 | +These classes stand in contrast to value types like `bool`, `int`, `Vector2`, `Color` etc., which are copied in GDScript, not referenced. In Rust, those types either map to built-in Rust types or structs implementing the `Copy` trait. |
| 6 | + |
| 7 | +## `Ref`: persistent reference |
| 8 | + |
| 9 | +The generic smart pointer `gdnative::Ref<T, Access>` allows you to store `Object` instances in Rust. It comes with different access policies, depending on how the memory of the underlying object is managed (consult [the docs](https://docs.rs/gdnative/latest/gdnative/struct.Ref.html) for details). Most of the time, you will be working with `Ref<T>`, which is the same as `Ref<T, Shared>` and the only access policy that is explained here. Its memory management mirrors that of the underlying type: |
| 10 | +* for all Godot objects inheriting the `Reference` class, `Ref<T>` is reference-counted like `Arc<T>` and will clean up automatically. |
| 11 | +* for all other types (i.e. the type `Object` and inheritors of `Node`), `Ref<T>` behaves like a raw pointer with manual memory management. |
| 12 | + |
| 13 | +For example, storing a reference to a Godot `Node2D` instance in a struct would look as follows: |
| 14 | +```rust |
| 15 | +struct GodotNode { |
| 16 | + node_ref: Ref<Node2D>, |
| 17 | +} |
| 18 | +``` |
| 19 | + |
| 20 | +_See `Ref` in |
| 21 | +[godot-rust docs](https://docs.rs/gdnative/latest/gdnative/struct.Ref.html)_ |
| 22 | + |
| 23 | + |
| 24 | +## `TRef`: temporary reference |
| 25 | + |
| 26 | +While `Ref` is a persistent pointer to retain references to Godot objects for an extended period of time, it doesn't grant access to the underlying Godot object. The reason for this is that `Ref` cannot generally guarantee that the underlying object, which is managed by the Godot engine, is valid at the time of the access. However, you as a user are in control of GDScript code and the scene tree, thus you can assert that an object is valid at a certain point in time by using `assume_safe()`. This is an unsafe function that returns a `gdnative::TRef<T, Access>` object, which allows you to call methods on the node. You are responsible for this assumption to be correct; violating it can lead to undefined behavior. |
| 27 | + |
| 28 | +The following example demonstrates `TRef`. A node is stored inside a Rust struct, and its position is modified through `set_position()`. This approach could be used in an ECS (Entity-Component-System) architecture, where `GodotNode` is a component, updated by a system. |
| 29 | +```rust |
| 30 | +struct GodotNode { |
| 31 | + node_ref: Ref<Node2D>, |
| 32 | +} |
| 33 | + |
| 34 | +fn update_position(node: &GodotNode) { |
| 35 | + let pos = Vector2::new(20, 30); |
| 36 | + |
| 37 | + // fetch temporary reference to the node |
| 38 | + let node: TRef<Node2D> = unsafe { node.node_ref.assume_safe() }; |
| 39 | + |
| 40 | + // call into the Godot engine |
| 41 | + // this implicitly invokes deref(), turning TRef<Node2D> into &Node2D |
| 42 | + node.set_position(pos); |
| 43 | +} |
| 44 | +``` |
| 45 | +Note that the parameter type is `&GodotNode`, not `&mut GodotNode`. Then why is it possible to mutate the Godot object? |
| 46 | + |
| 47 | +All Godot classes in Rust (`Object` and its subtypes) have only methods that operate on `&self`, not `&mut self`. The reason for this choice is that `&mut` is -- strictly speaking -- not a mutable reference, but rather an [_exclusive_ reference](https://docs.rs/dtolnay/latest/dtolnay/macro._02__reference_types.html). The one and only thing it guarantees is that while it exists, no other reference to the same object can exist (no aliasing). Since all the Godot classes can be shared with the Godot engine, which is written in C++ and allows free aliasing, using `&mut` references would potentially violate the exclusivity, leading to UB. This is why `&T` is used, and just like e.g. `&RefCell` it _does_ allow mutation. |
| 48 | + |
| 49 | +This being said, it can still make sense to bring back some type safety on a higher level in your own code. For example, you could make the `update_position()` take a `&mut GodotNode` parameter, to make sure that access to this `GodotNode` object is exclusive. |
| 50 | + |
| 51 | + |
| 52 | +_See `TRef` in |
| 53 | +[godot-rust docs](https://docs.rs/gdnative/latest/gdnative/struct.TRef.html)_ |
| 54 | + |
| 55 | + |
| 56 | +## `Instance`: reference with attached Rust class |
| 57 | + |
| 58 | +When working with classes that are provided by the engine or defined in GDScript, the `Ref` smart pointer is the ideal type for interfacing between Rust and Godot. However, when defining a custom class in Rust, that is registered with the Godot engine, there are two parts that need to be stored together: |
| 59 | + |
| 60 | +1. **GDNative script:** the Rust struct object that implements the entire custom logic. The Rust struct is written by you. |
| 61 | +1. **Base object:** the base class from which the script inherits, with its own state. This is always a Godot built-in class such as `Object`, `Reference` or `Node`. |
| 62 | + |
| 63 | +The `Instance` class simply wraps the two parts into a single type. |
| 64 | + |
| 65 | +When passing around your own Rust types, you will thus be working with `Instance`. The traits `ToVariant`, `FromVariant` and `OwnedToVariant` are automatically implemented for `Instance` types, allowing you to pass them from and to the Godot engine. |
| 66 | + |
| 67 | + |
| 68 | +### Construction |
| 69 | + |
| 70 | +Let's use a straightforward example: a player with name and score. Exported methods and properties are omitted for simplicity; the full interfacing will be explained later in [Calling into GDScript from Rust](../rust-binding/calling-gdscript.md). |
| 71 | +```rust |
| 72 | +#[derive(NativeClass)] |
| 73 | +// no #[inherit], thus inherits Reference by default |
| 74 | +pub struct Player { |
| 75 | + name: String, |
| 76 | + score: u32, |
| 77 | +} |
| 78 | + |
| 79 | +#[methods] |
| 80 | +impl Player { |
| 81 | + fn new(_owner: &Reference) -> Self { |
| 82 | + Self { |
| 83 | + name: "New player".to_string(), |
| 84 | + score: 0 |
| 85 | + } |
| 86 | + } |
| 87 | +} |
| 88 | +``` |
| 89 | + |
| 90 | +To create a default instance, use `Instance::new_instance()`. |
| 91 | +You can later use `map()` and `map_mut()` to access the `Instance` immutably and mutably. |
| 92 | + |
| 93 | +```rust |
| 94 | +let instance: Instance<Reference, Unique> = Instance::new(); |
| 95 | +// or: |
| 96 | +let instance = Player::new_instance(); |
| 97 | + |
| 98 | +// note: map_mut() takes &self, so above is not 'let mut' |
| 99 | +instance.map_mut(|p: &mut Player, _base: TRef<Reference, Unique>| { |
| 100 | + p.name = "Joe".to_string(); |
| 101 | + p.score = 120; |
| 102 | +}); |
| 103 | +``` |
| 104 | + |
| 105 | +If you don't need a Godot-enabled default constructor, use the `#[no_constructor]` attribute and define your own Rust `new()` constructor. |
| 106 | +```rust |
| 107 | +#[derive(NativeClass)] |
| 108 | +#[no_constructor] |
| 109 | +pub struct Player { |
| 110 | + name: String, |
| 111 | + score: u32, |
| 112 | +} |
| 113 | + |
| 114 | +#[methods] |
| 115 | +impl Player { |
| 116 | + pub fn new(name: &str, score: u32) -> Self { |
| 117 | + Self { name: name.to_string(), score } |
| 118 | + } |
| 119 | +} |
| 120 | +``` |
| 121 | + |
| 122 | +In this case, you can construct an `Instance` from an existing Rust object using `Instance::emplace()`: |
| 123 | +```rust |
| 124 | +let player = Player::new("Joe", 120); |
| 125 | + |
| 126 | +let instance = Instance::emplace(player); |
| 127 | +// or: |
| 128 | +let instance = player.emplace(); |
| 129 | +``` |
| 130 | + |
| 131 | + |
| 132 | + |
| 133 | + |
| 134 | +_See `Instance` in |
| 135 | +[godot-rust docs](https://docs.rs/gdnative/latest/gdnative/nativescript/struct.Instance.html)_ |
| 136 | + |
0 commit comments