-
-
Notifications
You must be signed in to change notification settings - Fork 210
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Array support #33
Comments
Note on |
Very good point. If e.g. array[i] *= 2 is not possible, we could think about a convenience function: array.edit(i, |e| e *= 2); |
Not sure if this makes it the "conventional" Rust name for such functions, but that signature looks pretty similar to the unstable I'm not exactly convinced of the "returns the new value" part of the |
So, I am not very experienced in programming, rust, or Godot, but I want to have a go at this issue. What I lack in knowledge, I can make up for in that I have a lot of free time. Can you give me some directions on how to get started? I run Fedora Workstation 37. I have cargo and godot installed. This is my first attempt at contributing to a project, so any help is much appreciated |
@Hanna-Kitten I would suggest starting with the non-generic (Variant) array type if you aren't very experienced with Rust. All you need to write is a bunch of simple wrappers around FFI functions. The You should expect some differences both in the definitions of the FFI functions themselves and in the internal APIs, but the basic idea is the same. The generic ones require a lot more engineering to get right, so I wouldn't suggest that to a beginner. |
@chitoyuu Thank you so much for your reply. I want to try writing some wrapper functions like you suggested, but I'm not sure what my first objective is. What should be my first goal/thing to implement (please try to be specific)? With this info I could focus on that idea, and hopefully I can use that knowledge to implement other aspects of this feature set. I'll need to read about rust/godot FFI/API too. I'm starting from zero in on these subjects. All code I've written to learn is self contained and single purposed and basic in nature. |
Try running Focus on the important things: you don't have to do everything all at once. It's likely that the internal API will see a lot of changes before any versioned release, anyway. It could be beneficial, even, to keep the surface small, so less need to be undone when (not if!) the time eventually comes.
The Rustonomicon has some information on FFI, but most of the machinery is done for you, so you shouldn't have to worry too much about it. Just call any appropriate As for Godot FFI particularly, the bad news is that it's severely undocumented. Often you'll have to dig into the engine source code to see if something you want to do is really safe. When in doubt, avoid dealing with lifetimes. |
85: Implement basic Array functionality r=Bromeon a=ttencate This implements the "easy" stuff on _variant_ arrays: - `FromIterator`, `From` on slices and Rust arrays - `IntoIterator`, `try_to_vec` - basic modification methods - most utility methods Still missing: - utility methods that require `Callable` - `Eq`, `Ord` (requires generated wrappers for operators) - everything involving "typed" and "readonly" See #33 Co-authored-by: Thomas ten Cate <[email protected]>
So we have a fully functional Typed arrays in GDScriptGDScript 2.0 introduced the notion of "typed arrays", which are a very limited form of generics. An This type consists of three parts:
Such a typed array is created in GDScript using a type annotation, and every attempt to put a value of the wrong type into it will log an error and continue as if it never happened:
Remember that arrays are invariant, not covariant. A dog may be an animal, but an array of dogs is not an array of animals. GDScript violates this principle:
Also keep in mind that all of the above is how it currently works; there is no spec or stability guarantee for GDScript. Mapping this to RustThere are basically three options. 1. Only variant arraysThe simplest approach would be to ignore the typing on arrays altogether. The only problem is that a typed array (which Godot 3 and thus We can work around that by explicitly type-checking everything that goes into the array, so that we can handle type errors before they get to Godot. Sadly, Godot will then just repeat the same check again. We would need to ensure (now and in the future) that our check is implemented in exactly the same way. 2. Variant and typed arraysIt would make sense to encode the runtime type of the array as a compile-type generic argument in Rust: The challenge is to make sure that the runtime type of the array always matches There is also a problem with nested arrays. In Rust, we can write var inner: Array[String] = ["The answer is:"]
var arr: Array[Array] = [inner]
obj.push_answer_to_first(arr) #[func]
fn push_answer_to_first(arr: TypedArray<TypedArray<i64>>) {
arr.get(0)
.push(42); // Whoops! Looks safe, but Godot prints an error, and nothing else happens.
} Since #[func]
fn push_answer_to_first(arr: TypedArray<TypedArray<i64>>) {
arr.get(0) // Unexpected panic! Expected array of INT, got array of STRING.
.push(42);
} 3. Variant, typed and unknown arraysTo avoid the above scenario, we could create a third type of array, the Instead of There are no more unexpected panics, but the drawback is, of course, more boilerplate when working with nested arrays: #[func]
fn push_answer_to_first(arr: TypedArray<UnknownArray>) {
arr.get(0)
.try_to_typed::<i64>()
.unwrap() // Panic! But that's what you expect from unwrap()!
.push(42);
} I don't think this edge case is worth the extra complexity though. |
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests - Add `array!` and also `dict!` in the prelude See godot-rust#33 for design discussion
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests - Add `array!` and also `dict!` in the prelude See godot-rust#33 for design discussion
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests See godot-rust#33 for design discussion
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests See godot-rust#33 for design discussion
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests See godot-rust#33 for design discussion
A few cleanup actions remain once #101 is merged:
|
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests See godot-rust#33 for design discussion
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests See godot-rust#33 for design discussion
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests See godot-rust#33 for design discussion
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests See godot-rust#33 for design discussion
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests See godot-rust#33 for design discussion
A generic
The generic arguments need to be repeated for each inner macro invocation, but such "nested loops" are not supported in declarative macros:
If we want this, I think we'll need to resort to a procedural macro. Maybe even support something like |
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests See godot-rust#33 for design discussion
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests See godot-rust#33 for design discussion
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests See godot-rust#33 for design discussion
- Rename `Array` to `TypedArray<T>` - Check/set runtime type of the underlying Godot `Array` - Make all parameters and return values `T` instead of `Variant` - Add `Array` as an alias for `TypedArray<Variant>` - Add `array!` macro and use it in tests See godot-rust#33 for design discussion
122: Implement PartialEq for Packed*Array and Eq where appropriate r=Bromeon a=ttencate See #33. Co-authored-by: Thomas ten Cate <[email protected]>
Following the previous proposal, a new idea came up in a Discord discussion. The core observation is that arrays are still convariant when looking at read operations. As a consequence, instead of Brainstorming:
|
Found a bug on our end: this check is not actually called when calling a That aside, let me try to understand your proposal: fn accept_array<A: ToReadArray>(array: A) {
let array: ReadOnlyArray = array.read_only(); // upcast
...
} Why is there a generic there? Is it for calling
¹ Since arrays are not covariant, it's not really upcasting, it's a conversion between two disjoint types, not unlike a |
Good catch. I think the whole binding interface should be hardened, I created #125 to track it.
Sorry for confusion, the // forget about naming
trait ToReadArray {
fn read_only() -> ReadOnlyArray;
} So it would not offer any functionality on its own, but allow converting/"upcasting". Why not a |
I think a full code example would be helpful to flesh this out. All the type definitions involved, as well as a basic example use case. |
Agree, thanks for the reminder! And huge thanks for contributing so much to array support! 👍 |
Implement a richer API for arrays -- both
Array
(containingVariant
) andTypedArray<T>
(containing concrete types).Inspiration can be found in
gdnative::core_types::PoolArray
, as well as Godot'sArray
documentation.Some ideas, can also be implemented as separate pull requests:
Index
,IntoIterator
,PartialEq
,PartialOrd
impl_builtin_traits!
for the "object relation" traitsFrom<Vec<T>>
, also betweenArray
andTypedArray
(one way fallible)shuffle()
,get_typed_builtin()
, ...slice()
,size()
,sort_custom()
, ...If possible, additions should be accompanied by unit tests. Rather less functionality but properly tested than feature-creep 🙂
The text was updated successfully, but these errors were encountered: