|
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