-
-
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
Implement basic Array functionality #85
Conversation
93da33c
to
205da48
Compare
2897b51
to
abd32dc
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a fantastic contribution, thanks a lot! ❤️
Very sensible choice of rust-idiomatic vs. true-to-GDScript signatures, something that's not easy. Also great test coverage! 🙂
You seem to use a lot of try_into().unwrap()
, maybe extract that into functions to_i64
/to_isize
? We could then later refactor it to unwrap_unchecked
if at some point we can't afford the unwrap
anymore.
bors try
/// # Safety | ||
/// | ||
/// Usage is safe if the `Array` is used on a single thread only. Concurrent reads on different | ||
/// threads are also safe, but any writes must be externally synchronized. The Rust compiler will | ||
/// enforce this as long as you use only Rust threads, but it cannot protect against concurrent | ||
/// modification on other threads (e.g. created through GDScript). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for adding this section! 👍
Concurrent reads on different threads are also safe, but any writes must be externally synchronized.
Isn't that always true, for any kind of data structure? If all the accesses are read-only, threading is never a problem, many functional languages are built on this principle.
Is the safety-related information specific to Array
or should we maybe move it one level up, to the builtin
module documentation? I'm also writing an entire article about safety in godot-rust, so the API doc can really go into specifics and can take high-level assumptions (don't mess up threading from GDScript) as a base. I guess it doesn't hurt to repeat some parts, maybe later we can link to that article.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing worth mentioning is that each Array
instance lives independently and has copy semantics (in fact CoW, but from the user perspective, copying an array creates an independent on).
Can you imagine any scenario where this leads to safety issues? E.g. the old instance is iterated over, but a copy (yet-to-be-CoW-written) is now modified.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Isn't that always true, for any kind of data structure? If all the accesses are read-only, threading is never a problem, many functional languages are built on this principle.
It's true that concurrent reads are normally safe, but concurrent writes can also be made safe internally (using mutexes or whatever). The main point is that we don't do that.
Is the safety-related information specific to Array or should we maybe move it one level up, to the
builtin
module documentation?
I just wrote it here for now because I felt bad about ignoring GDScript threads altogether, whereas gdnative's VariantArray
handles them more explicitly. But I didn't feel qualified to make such safety claims for other parts of the gdextension :) If it needs to go somewhere, I'd expect it even higher up, at the top level.
One thing worth mentioning is that each
Array
instance lives independently and has copy semantics (in fact CoW, but from the user perspective, copying an array creates an independent on).
Array
is a strange beast. It's refcounted, so writes are reflected in all copies, but the refcounted thing is Vector<Variant>
which itself is a refcounted copy-on-write array.
So the question is: can it ever happen that the inner Vector<T>
is shared between two seemingly independent instances of Array
? I don't think so, but I wouldn't bet on it.
And the other question is: does it matter? Surely CowData
would create a copy before applying any mutations, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't see through the entire Array
implementation in Godot... Would need to spend more time on it.
What I noticed:
- Two instances share state (the ref-counter), when one instance is copied from the other. But it looks like Godot uses the
SafeRefCount
class, which usesstd::atomic
internally. So concurrent modifications of the ref-counter should not cause race conditions or UB. Array
has different methods that look like copying:_assign(const Array&) -> bool
- `typed_assign(const Array&) -> bool)
duplicate() -> Array
Array(const Array &p_from)
I'm not sure which one is used by the FFI copy constructor method, would need to run the debugger.
I think we're good for now. Let's get this initial implementation running.
/// Sorts the array. | ||
/// | ||
/// Note: The sorting algorithm used is not | ||
/// [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability). This means that values | ||
/// considered equal may have their order changed when using `sort_unstable`. | ||
pub fn sort_unstable(&mut self) { | ||
self.as_inner().sort(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very nice touch to change the name! 👍
This made me think: do we have variant types which can be "equivalent but not equal"?
Most types are Copy
, but maybe OBJECT
?
Would probably need some digging in Godot to be sure, but this name is a safe start...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe int(1)
and float(1.0)
? Or StringName("")
and String("")
?
tryBuild succeeded: |
Done! More readable too. All other comments addressed as well. |
e1fab0d
to
adf1b11
Compare
adf1b11
to
bf50b0d
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for the updates!
bors r+
Build succeeded:
|
This implements the "easy" stuff on variant arrays:
FromIterator
,From
on slices and Rust arraysIntoIterator
,try_to_vec
Still missing:
Callable
Eq
,Ord
(requires generated wrappers for operators)See #33