Skip to content

Commit 2bfa40c

Browse files
authored
Merge pull request #33 from jacobsky/faq-from-issues
Updated the FAQ and changed some of the formatting.
2 parents 7d4a6e6 + aa77f87 commit 2bfa40c

File tree

7 files changed

+657
-198
lines changed

7 files changed

+657
-198
lines changed

src/SUMMARY.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@
1313
- [Exported properties](./rust-binding/properties.md)
1414
- [Calling into GDScript from Rust](./rust-binding/calling-gdscript.md)
1515
- [FAQ](./faq.md)
16+
- [Code Questions](./faq/code.md)
17+
- [Multithreading](./faq/multithreading.md)
18+
- [Configuration](./faq/configuration.md)
19+
- [Versioning and supported platforms](./faq/meta.md)
20+
- [Community](./faq/community.md)
1621
- [(TODO) Testing](./testing.md)
1722
- [Structuring Code for Testing](./testing/structure.md)
1823
- [Testing with the Engine](./testing/engine.md)
1924
- [Recipes](./recipes.md)
2025
- [Rust Panic Hook](./recipes/rust_panic_handler.md)
26+
- [Logging](./recipes/logging.md)
2127
- [Exporting](./exporting.md)
2228
- [Android](./exporting/android.md)
2329
- [(TODO) iOS](./exporting/ios.md)

src/faq.md

Lines changed: 2 additions & 198 deletions
Original file line numberDiff line numberDiff line change
@@ -1,201 +1,5 @@
11
# FAQ
22

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.
44

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-
![no-completion](images/no-completion.png)
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-
![completion](images/completion.png)
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

Comments
 (0)