|
1 | 1 | # FAQ |
2 | 2 |
|
3 | | -## Avoiding a `BorrowFailed` error on method call |
| 3 | +This is a list of frequently asked questions that have been pulled from various sources. This will be periodically updated with new information. |
4 | 4 |
|
5 | | -**Question** |
6 | | - |
7 | | -What is the `BorrowFailed` error and why do I keep getting it? I'm only trying to call another method that takes `&mut self` while holding one! |
8 | | - |
9 | | -**Answer** |
10 | | - |
11 | | -In Rust, [there can only be *one* `&mut` reference to the same memory location at the same time](https://docs.rs/dtolnay/0.0.9/dtolnay/macro._02__reference_types.html). To enforce this while making simple use cases easier, the bindings make use of [interior mutability](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html). This works like a lock: whenever a method with `&mut self` is called, it will try to obtain a lock on the `self` value, and hold it *until it returns*. As a result, if another method that takes `&mut self` is called in the meantime for whatever reason (e.g. signals), the lock will fail and an error (`BorrowFailed`) will be produced. |
12 | | - |
13 | | -It's relatively easy to work around this problem, though: Because of how the user-data container works, it can only see the outermost layer of your script type - the entire structure. This is why it's stricter than what is actually required. If you run into this problem, you can [introduce finer-grained interior mutability](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html) in your own type, and modify the problematic exported methods to take `&self` instead of `&mut self`. |
14 | | - |
15 | | -## Passing additional arguments to a class constructor |
16 | | - |
17 | | -**Question** |
18 | | - |
19 | | -A native script type needs to implement `fn new(owner: &Node) -> Self`. |
20 | | -Is it possible to pass additional arguments to `new`? |
21 | | - |
22 | | -**Answer** |
23 | | - |
24 | | -Unfortunately this is currently a general limitation of GDNative (see [related issue](https://github.com/godotengine/godot/issues/23260)). |
25 | | - |
26 | | -As a result, a common pattern to work-around the limitation is to use explicit initialization methods. For instance: |
27 | | - |
28 | | -```rust |
29 | | -struct EnemyData { |
30 | | - name: String, |
31 | | - health: f32, |
32 | | -} |
33 | | - |
34 | | -#[derive(NativeClass)] |
35 | | -#[inherit(Object)] |
36 | | -struct Enemy { |
37 | | - data: Option<EnemyData>, |
38 | | -} |
39 | | - |
40 | | -#[methods] |
41 | | -impl Enemy { |
42 | | - fn new(_owner: &Object) -> Self { |
43 | | - Enemy { |
44 | | - data: None, |
45 | | - } |
46 | | - } |
47 | | - |
48 | | - #[export] |
49 | | - fn set_data(&mut self, _owner: &Object, name: String, health: f32) { |
50 | | - self.data = Some(EnemyData { name, health }); |
51 | | - } |
52 | | -} |
53 | | -``` |
54 | | - |
55 | | -This however has two disadvantages: |
56 | | -1. You need to use an `Option` with the sole purpose of late initialization, and subsequent `unwrap()` calls or checks -- weaker invariants in short. |
57 | | -1. An additional type `EnemyData` for each native class like `Enemy` is required (unless you have very few properties, or decide to add `Option` for each of them, which has its own disadvantages). |
58 | | - |
59 | | -An alternative is to register a separate factory class, which returns fully-constructed instances: |
60 | | -```rust |
61 | | -#[derive(NativeClass)] |
62 | | -#[no_constructor] // disallow default constructor |
63 | | -#[inherit(Object)] |
64 | | -struct Enemy { |
65 | | - name: String, |
66 | | - health: f32, |
67 | | -} |
68 | | - |
69 | | -#[methods] |
70 | | -impl Enemy { |
71 | | - // nothing here |
72 | | -} |
73 | | - |
74 | | -#[derive(NativeClass)] |
75 | | -#[inherit(Reference)] |
76 | | -struct EntityFactory {} |
77 | | - |
78 | | -#[methods] |
79 | | -impl EntityFactory { |
80 | | - #[export] |
81 | | - fn enemy(&self, _owner: &Object, name: String, health: f32) |
82 | | - -> Instance<Enemy, Unique> { |
83 | | - Enemy { name, health }.emplace() |
84 | | - } |
85 | | -} |
86 | | -``` |
87 | | -So instead of `Enemy.new()` you can write `EntityFactory.enemy(args)` in GDScript. |
88 | | -This still needs an extra type `EntityFactory`, however you could reuse that for multiple classes. |
89 | | - |
90 | | - |
91 | | -## Static methods |
92 | | - |
93 | | -**Question** |
94 | | - |
95 | | -In GDScript, classes can have static methods. |
96 | | -However, when I try to omit `self` in the exported method signature, I'm getting a compile error. |
97 | | -How can I implement a static method? |
98 | | - |
99 | | -**Answer** |
100 | | - |
101 | | -This is another limitation of GDNative -- static methods are not supported in general. |
102 | | - |
103 | | -As a work-around, it is possible to use a ZST (zero-sized type): |
104 | | - |
105 | | -```rust |
106 | | -#[derive(NativeClass, Copy, Clone, Default)] |
107 | | -#[user_data(Aether<StaticUtil>)] |
108 | | -#[inherit(Object)] |
109 | | -pub struct StaticUtil; |
110 | | - |
111 | | -#[methods] |
112 | | -impl StaticUtil { |
113 | | - #[export] |
114 | | - fn compute_something(&self, _owner: &Object, input: i32) -> i32 { |
115 | | - godot_print!("pseudo-static computation"); |
116 | | - 2 * input |
117 | | - } |
118 | | -} |
119 | | -``` |
120 | | - |
121 | | -[`Aether`](https://docs.rs/gdnative/0.9/gdnative/prelude/struct.Aether.html) is a special user-data wrapper intended for zero-sized types, that does not perform any allocation or synchronization at runtime. |
122 | | - |
123 | | -The type needs to be instantiated somewhere on GDScript level. |
124 | | -Good places for instantiation are for instance: |
125 | | - |
126 | | -- as a member of a long-living util object, |
127 | | -- as a [singleton auto-load object](https://docs.godotengine.org/en/stable/getting_started/step_by_step/singletons_autoload.html). |
128 | | - |
129 | | - |
130 | | -## Converting a Godot type to the underlying Rust type |
131 | | - |
132 | | -**Question** |
133 | | - |
134 | | -I have a method that takes an argument `my_object` as a `Variant`. |
135 | | -I know that this object has a Rust native script attached to it, called say `MyObject`. |
136 | | -How can I access the Rust type given the Variant? |
137 | | - |
138 | | -**Answer** |
139 | | - |
140 | | -This conversion can be accomplished by casting the `Variant` to a `Ref`, and then to an `Instance` or `RefInstance`, and mapping over it to access the Rust data type: |
141 | | - |
142 | | -```rust |
143 | | -#[methods] |
144 | | -impl AnotherNativeScript { |
145 | | - |
146 | | - #[export] |
147 | | - pub fn method_accepting_my_object(&self, _owner: &Object, my_object: Variant) { |
148 | | - // 1. Cast Variant to Ref of associated Godot type, and convert to TRef. |
149 | | - let my_object = unsafe { |
150 | | - my_object |
151 | | - .try_to_object::<Object>() |
152 | | - .expect("Failed to convert my_object variant to object") |
153 | | - .assume_safe() |
154 | | - }; |
155 | | - // 2. Obtain a RefInstance. |
156 | | - let my_object = my_object |
157 | | - .cast_instance::<MyObject>() |
158 | | - .expect("Failed to cast my_object object to instance"); |
159 | | - // 3. Map over the RefInstance to extract the underlying user data. |
160 | | - my_object |
161 | | - .map(|my_object, _owner| { |
162 | | - // Now my_object is of type MyObject. |
163 | | - }) |
164 | | - .expect("Failed to map over my_object instance"); |
165 | | - } |
166 | | - |
167 | | -} |
168 | | - |
169 | | -``` |
170 | | - |
171 | | -## Auto-completion with rust-analyzer |
172 | | - |
173 | | -**Question** |
174 | | - |
175 | | -`godot-rust` generates most of the gdnative type's code at compile-time. Editors using [rust-analyzer](https://github.com/rust-analyzer/rust-analyzer) struggle to autocomplete those types: |
176 | | - |
177 | | - |
178 | | - |
179 | | -**Answer** |
180 | | - |
181 | | -People [reported](https://github.com/rust-analyzer/rust-analyzer/issues/5040) similar issues and found that switching on the `"rust-analyzer.cargo.loadOutDirsFromCheck": true` setting fixed it: |
182 | | - |
183 | | - |
184 | | - |
185 | | - |
186 | | -## Auto-completion with IntelliJ Rust Plugin |
187 | | - |
188 | | -**Question** |
189 | | - |
190 | | -Similar to rust-analyzer, IntelliJ-Family IDEs struggle to autocomplete gdnative types generated at compile-time. |
191 | | - |
192 | | -**Answer** |
193 | | - |
194 | | -There are two problems preventing autocompletion of gdnative types in IntelliJ-Rust. |
195 | | - |
196 | | -First, the features necessary are (as of writing) considered experimental and must be enabled. Press `shift` twice to open the find all dialog and type `Experimental features...` and click the checkbox for `org.rust.cargo.evaluate.build.scripts`. Note that `org.rust.cargo.fetch.out.dir` will also work, but is known to be less performant and may be phased out. |
197 | | - |
198 | | -Second, the bindings files generated (~8mb) are above the 2mb limit for files to be processed. As [reported](https://github.com/intellij-rust/intellij-rust/issues/6571#) you can increase the limit with the steps below. |
199 | | -* open custom VM options dialog (`Help` | `Find Action` and type `Edit Custom VM Options`) |
200 | | -* add `-Didea.max.intellisense.filesize=limitValue` line where `limitValue` is desired limit in KB, for example, 10240. Note, it cannot be more than 20 MB. |
201 | | -* restart IDE |
| 5 | +Please select one of the categories in the side bar for more information. |
0 commit comments