1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
/*
* Copyright (c) godot-rust; Bromeon and contributors.
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
//! # Extended documentation
//!
//! This highlights a few concepts in the public API of the `godot` crate. They complement information
//! available on the main crate documentation page and the book.
//!
//! ## Type categories
//!
//! Godot is written in C++, which doesn't have the same strict guarantees about safety and
//! mutability that Rust does. As a result, not everything in this crate will look and feel
//! entirely "rusty". See also [Philosophy](https://godot-rust.github.io/book/contribute/philosophy.html).
//!
//! Traits such as `Clone`, `PartialEq` or `PartialOrd` are designed to mirror Godot semantics,
//! except in cases where Rust is stricter (e.g. float ordering). Cloning a type results in the
//! same observable behavior as assignment or parameter-passing of a GDScript variable.
//!
//! We distinguish four different kinds of types:
//!
//! 1. **Value types**: `i64`, `f64`, and mathematical types like
//! [`Vector2`][crate::builtin::Vector2] and [`Color`][crate::builtin::Color].
//!
//! These are the simplest to understand and to work with. They implement `Clone` and often
//! `Copy` as well. They are implemented with the same memory layout as their counterparts in
//! Godot itself, and typically have public fields. <br><br>
//!
//! 2. **Copy-on-write types**: [`GString`][crate::builtin::GString],
//! [`StringName`][crate::builtin::StringName], and [`PackedArray`][crate::builtin::PackedArray] types.
//!
//! These mostly act like value types, similar to Rust's own `Vec`. You can `Clone` them to get
//! a full copy of the entire object, as you would expect.
//!
//! Under the hood in Godot, these types are implemented with copy-on-write, so that data can be
//! shared until one of the copies needs to be modified. However, this performance optimization
//! is entirely hidden from the API and you don't normally need to worry about it. <br><br>
//!
//! 3. **Reference-counted types**: [`Array`][crate::builtin::Array],
//! [`VarDictionary`][crate::builtin::VarDictionary], and [`Gd<T>`][crate::obj::Gd] where `T` inherits
//! from [`RefCounted`][crate::classes::RefCounted].
//!
//! These types may share their underlying data between multiple instances: changes to one
//! instance are visible in another. They are conceptually similar to `Rc<RefCell<...>>`.
//!
//! Since there is no way to prevent or even detect this sharing from Rust, you need to be more
//! careful when using such types. For example, when iterating over an `Array`, make sure that
//! it isn't being modified at the same time through another reference.
//!
//! `Clone::clone()` on these types creates a new reference to the same instance, while
//! type-specific methods such as [`Array::duplicate_deep()`][crate::builtin::Array::duplicate_deep]
//! can be used to make actual copies. <br><br>
//!
//! 4. **Manually managed types**: [`Gd<T>`][crate::obj::Gd] where `T` inherits from
//! [`Object`][crate::classes::Object] but not from [`RefCounted`][crate::classes::RefCounted];
//! most notably, this includes all `Node` classes.
//!
//! These also share data, but do not use reference counting to manage their memory. Instead,
//! you must either hand over ownership to Godot (e.g. by adding a node to the scene tree) or
//! free them manually using [`Gd::free()`][crate::obj::Gd::free]. <br><br>
//!
//!
//! ## Ergonomics and panics
//!
//! gdext is designed with usage ergonomics in mind, making it viable for fast prototyping.
//! Part of this design means that users should not constantly be forced to write code such as
//! `obj.cast::<T>().unwrap()`. Instead, they can just write `obj.cast::<T>()`, which may panic at runtime.
//!
//! This approach has several advantages:
//! * The code is more concise and less cluttered.
//! * Methods like `cast()` provide very sophisticated panic messages when they fail (e.g. involved
//! classes), immediately giving you the necessary context for debugging. This is certainly
//! preferable over a generic `unwrap()`, and in most cases also over a `expect("literal")`.
//! * Usually, such methods panicking indicate bugs in the application. For example, you have a static
//! scene tree, and you _know_ that a node of certain type and name exists. `get_node_as::<T>("name")`
//! thus _must_ succeed, or your mental concept is wrong. In other words, there is not much you can
//! do at runtime to recover from such errors anyway; the code needs to be fixed.
//!
//! Now, there are of course cases where you _do_ want to check certain assumptions dynamically.
//! Imagine a scene tree that is constructed at runtime, e.g. in a game editor.
//! This is why the library provides "overloads" for most of these methods that return `Option` or `Result`.
//! Such methods have more verbose names and highlight the attempt, e.g. `try_cast()`.
//!
//! To help you identify panicking methods, we use the symbol "⚠️" at the beginning of the documentation;
//! this should also appear immediately in the auto-completion of your IDE. Note that this warning sign is
//! not used as a general panic indicator, but particularly for methods which have a `Option`/`Result`-based
//! overload. If you want to know whether and how a method can panic, check if its documentation has a
//! _Panics_ section.
//! <br><br>
//!
//!
//! ## Thread safety
//!
//! [Godot's own thread safety
//! rules](https://docs.godotengine.org/en/latest/tutorials/performance/thread_safe_apis.html)
//! apply. Types in this crate implement (or don't implement) `Send` and `Sync` wherever
//! appropriate, but the Rust compiler cannot check what happens to an object through C++ or
//! GDScript.
//!
//! As a rule of thumb, if you must use threading, prefer to use [Rust threads](https://doc.rust-lang.org/std/thread)
//! over Godot threads.
//!
//! The Cargo feature `experimental-threads` provides experimental support for multithreading. The underlying safety
//! rules are still being worked out, as such you may encounter unsoundness and an unstable API.
//!
//!
//! ## Builtin API Design
//!
//! See also [`godot::builtin`](crate::builtin) module documentation.
//!
//! Our goal is to strive for a middle ground between idiomatic Rust and existing Godot APIs, achieving a decent balance between ergonomics,
//! correctness and performance. We leverage Rust's type system (such as `Option<T>` or `enum`) where it helps expressivity.
//!
//! We have been using a few guiding principles. Those apply to builtins in particular, but some are relevant in other modules, too.
//!
//! ### 1. `Copy` for value types
//!
//! _Value types_ are types with public fields and no hidden state. This includes all geometric types, colors and RIDs.
//!
//! All value types implement the `Copy` trait and thus have no custom `Drop` impl.
//!
//! ### 2. By-value (`self`) vs. by-reference (`&self`) receivers
//!
//! Most `Copy` builtins use by-value receivers. The exception are matrix-like types (e.g., `Basis`, `Transform2D`, `Transform3D`, `Projection`),
//! whose methods operate on `&self` instead. This is close to how the underlying `glam` library handles it.
//!
//! ### 3. `Default` trait only when the default value is common and useful
//!
//! `Default` is deliberately not implemented for every type. Rationale:
//! - For some types, the default representation (as per Godot) does not constitute a useful value. This goes against Rust's [`Default`] docs,
//! which explicitly mention "A trait for giving a type a _useful_ default value". For example, `Plane()` in GDScript creates a degenerate
//! plane which cannot participate in geometric operations.
//! - Not providing `Default` makes users double-check if the value they want is indeed what they intended. While it seems convenient, not
//! having implicit default or "null" values is a design choice of Rust, avoiding the Billion Dollar Mistake. In many situations, `Option` or
//! [`OnReady`][crate::obj::OnReady] is a better alternative.
//! - For cases where the Godot default is truly desired, we provide an `invalid()` constructor, e.g. `Callable::invalid()` or `Plane::invalid()`.
//! This makes it explicit that you're constructing a value that first has to be modified before becoming useful. When used in class fields,
//! `#[init(val = ...)]` can help you initialize such values.
//! - Outside builtins, we do not implement `Gd::default()` for manually managed types, as this makes it very easy to overlook initialization
//! (e.g. in `#[derive(Default)]`) and leak memory. A `Gd::new_alloc()` is very explicit.
//!
//! ### 4. Prefer explicit conversions over `From` trait
//!
//! `From` is quite popular in Rust, but unlike traits such as `Debug`, the convenience of `From` can come at a cost. Like every feature, adding
//! an `impl From` needs to be justified -- not the other way around: there doesn't need to be a particular reason why it's _not_ added. But
//! there are in fact some trade-offs to consider:
//!
//! 1. `From` next to named conversion methods/constructors adds another way to do things. While it's sometimes good to have choice, multiple
//! ways to achieve the same has downsides: users wonder if a subtle difference exists, or if all options are in fact identical.
//! It's unclear which one is the "preferred" option. Recognizing other people's code becomes harder, because there tend to be dialects.
//! 2. It's often a purely stylistic choice, without functional benefits. Someone may want to write `(1, 2).into()` instead of
//! `Vector2i::new(1, 2)`. This is not strong enough of a reason -- if brevity is of concern, a function `vec2i(1, 2)` does the job better.
//! 3. `From` is less explicit than a named conversion function. If you see `string.to_variant()` or `color.to_hsv()`, you immediately
//! know the target type. `string.into()` and `color.into()` lose that aspect. Even with `(1, 2).into()`, you'd first have to check whether
//! `From` is only converting the tuple, or if it _also_ provides an `i32`-to-`f32` cast, thus resulting in `Vector2` instead of `Vector2i`.
//! This problem doesn't exist with named constructor functions.
//! 4. The `From` trait doesn't play nicely with type inference. If you write `let v = string.to_variant()`, rustc can infer the type of `v`
//! based on the right-hand expression alone. With `.into()`, you need follow-up code to determine the type, which may or may not work.
//! Temporarily commenting out such non-local code breaks the declaration line, too. To make matters worse, turbofish `.into::<Type>()` isn't
//! possible either.
//! 5. Rust itself [requires](https://doc.rust-lang.org/std/convert/trait.From.html#when-to-implement-from) that `From` conversions are
//! infallible, lossless, value-preserving and obvious. This rules out a lot of scenarios such as `DynGd::to_gd()` (which only maintains
//! the class part, not trait) or `Color::try_to_hsv()` (which is fallible and lossy).
//!
//! One main reason to support `From` is to allow generic programming, in particular `impl Into<T>` parameters. This is also the reason
//! why the string types have historically implemented the trait. But this became less relevant with the advent of
//! [`AsArg<T>`][crate::meta::AsArg] taking that role, and thus may change in the future.
//!
//! ### 5. `Option` for fallible operations
//!
//! GDScript often uses degenerate types and custom null states to express that an operation isn't successful. This isn't always consistent:
//! - [`Rect2::intersection()`] returns an empty rectangle (i.e. you need to check its size).
//! - [`Plane::intersects_ray()`] returns a `Variant` which is NIL in case of no intersection. While this is a better way to deal with it,
//! it's not immediately obvious that the result is a point (`Vector2`), and comes with extra marshaling overhead.
//!
//! Rust uses `Option` in such cases, making the error state explicit and preventing that the result is accidentally interpreted as valid.
//!
//! [`Rect2::intersection()`]: https://docs.godotengine.org/en/stable/classes/class_rect2.html#class-rect2-method-intersection
//! [`Plane::intersects_ray()`]: https://docs.godotengine.org/en/stable/classes/class_plane.html#class-plane-method-intersects-ray
//!
//! ### 6. Public fields and soft invariants
//!
//! Some geometric types are subject to "soft invariants". These invariants are not enforced at all times but are essential for certain
//! operations. For example, bounding boxes must have non-negative volume for operations like intersection or containment checks. Planes
//! must have a non-zero normal vector.
//!
//! We cannot make them hard invariants (no invalid value may ever exist), because that would disallow the convenient public fields, and
//! it would also mean every value coming over the FFI boundary (e.g. an `#[export]` field set in UI) would constantly need to be validated
//! and reset to a different "sane" value.
//!
//! For **geometric operations**, Godot often doesn't specify the behavior if values are degenerate, which can propagate bugs that then lead
//! to follow-up problems. godot-rust instead provides best-effort validations _during an operation_, which cause panics if such invalid states
//! are detected (at least in Debug mode). Consult the docs of a concrete type to see its guarantees.
//!
//! ### 7. RIIR for some, but not all builtins
//!
//! Builtins use varying degrees of Rust vs. engine code for their implementations. This may change over time and is generally an implementation
//! detail.
//!
//! - 100% Rust, often supported by the `glam` library:
//! - all vector types (`Vector2`, `Vector2i`, `Vector3`, `Vector3i`, `Vector4`, `Vector4i`)
//! - all bounding boxes (`Rect2`, `Rect2i`, `Aabb`)
//! - 2D/3D matrices (`Basis`, `Transform2D`, `Transform3D`)
//! - `Plane`
//! - `Rid` (just an integer)
//! - Partial Rust: `Color`, `Quaternion`, `Projection`
//! - Only Godot FFI: all others (containers, strings, callables, variant, ...)
//!
//! The rationale here is that operations which are absolutely ubiquitous in game development, such as vector/matrix operations, benefit
//! a lot from being directly implemented in Rust. This avoids FFI calls, which aren't necessarily slow, but remove a lot of optimization
//! potential for rustc/LLVM.
//!
//! Other types, that are used less in bulk and less often in performance-critical paths (e.g. `Projection`), partially fall back to Godot APIs.
//! Some operations are reasonably complex to implement in Rust, and we're not a math library, nor do we want to depend on one besides `glam`.
//! An ever-increasing maintenance burden for geometry re-implementations is also detrimental.
//!
//! TLDR: it's a trade-off between performance, maintenance effort and correctness -- the current combination of `glam` and Godot seems to be a
//! relatively well-working sweet spot.
//!
//! ### 8. `glam` types are not exposed in public API
//!
//! While Godot and `glam` share common operations, there are also lots of differences and Godot specific APIs.
//! As a result, godot-rust defines its own vector and matrix types, making `glam` an implementation details.
//!
//! Alternatives considered:
//!
//! 1. Re-export types of an existing vector algebra crate (like `glam`).
//! The `gdnative` crate started out this way, using types from `euclid`, but [became impractical](https://github.com/godot-rust/gdnative/issues/594#issue-705061720).
//! Even with extension traits, there would be lots of compromises, where existing and Godot APIs differ slightly.
//!
//! Furthermore, it would create a strong dependency on a volatile API outside our control. `glam` had 9 SemVer-breaking versions over the
//! timespan of two years (2022-2024). While it's often easy to migrate and the changes notably improve the library, this would mean that any
//! breaking change would also become breaking for godot-rust, requiring a SemVer bump. By abstracting this, we can have our own timeline.
//!
//! 2. We could opaquely wrap types, i.e. `Vector2` would contain a private `glam::Vec2`. This would prevent direct field access, which is
//! _extremely_ inconvenient for vectors. And it would still require us to redefine the front-end of the entire API.
//!
//! Eventually, we might add support for [`mint`](https://crates.io/crates/mint) to allow conversions to other linear algebra libraries in the
//! ecosystem. (Note that `mint` intentionally offers no math operations, see e.g. [mint#75](https://github.com/kvark/mint/issues/75)).