-
Notifications
You must be signed in to change notification settings - Fork 35
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
Introduce r_array::TypedFrozenRArray
and r_array::Iter
#85
base: main
Are you sure you want to change the base?
Conversation
279eb09
to
fd68afb
Compare
So I have to admit that every now and again I check GitHub's dependents view and nose around other people's projects to see how they are using Magnus. I happened to be looking at bluejay-rb (very cool btw) just the other day and particularly at your One thing I'm not keen on is implementing I was also thinking it might be possible to have a typed array without freezing it, here's a rough design (I've not checked if it works/compiles):
Possibly the two ideas aren't mutually exclusive? As for the iterator, yeah, I think the performance of It's a bit wonky with the way Magnus's types are all |
I've added the I'm not sure if that covers your use case? |
fd68afb
to
0be4b23
Compare
Sorry for the delayed reply! Thanks for adding In the most recent commit, I tried to address the feedback, including:
Thanks! |
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 think with your Iter<T>::next
it should fetch the len on every iteration. I would hope that the compiler knows that the len is behind a pointer which could be mutated at any time, so won't optimise out the repeated calls.
So I don't think the possibility of modifying an Array while also using RArray::iter
should cause it to be marked unsafe
as I don't think it could lead to a memory safety violation.
But as that len check is chasing a pointer on every iteration I wonder if making len a field of Iter
and passing it in when construing an Iter
would be able to avoid that repeated pointer following and win some performance. If you were to make that change then RArray::iter
would need to be unsafe.
I didn't think of using TryFrom
for getting a TypedArray
from an RArray
. I probably skipped over that option because I've moved away from using From
/Into
for creating Ruby types as they could be used from non-Ruby threads. That's not a worry in this case as it's converting from one Ruby type to another, so if you have a Ruby type to start you know you're on a Ruby thread.
I think it'd be best if it was consistent on how you get ahold of TypedArray
and TypedFrozenArray
, but I'm not strongly wedded to one approach over the other. We can swap the typecheck
method for an implementation of TryFrom
.
I feel inherent methods are a little more discoverable than trait implementations, what are your thoughts?
/// ``` | ||
pub fn typed_frozen_ary_new<T>(&self) -> TypedFrozenRArray<T> | ||
where | ||
T: TryConvert + Copy, |
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.
You can avoid the extra + Copy
bound here (and in a couple of other spots) if, instead of deriving, you manually implement Copy/Clone like:
impl<T> Copy for TypedFrozenRArray<T> {}
impl<T> Clone for TypedFrozenRArray<T> {
fn clone(&self) -> Self {
*self
}
}
/// # Ruby::init(example).unwrap() | ||
/// ``` | ||
fn into_iter(self) -> Self::IntoIter { | ||
Iter::new(self.to_r_array()) |
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 either needs to be frozen, or cast to an RArray
(like unsafe { RArray::from_value_unchecked(self.0.get()) }
without calling to_r_array
. As soon as to_r_array
is called the array becomes visible to Ruby via ObjectSpace.each_object
(even if it's never returned to Ruby), so could be mutated to contain a non-T
value, but this iterator is promising a T
.
I have found having a typed frozen wrapper around
RArray
to be quite useful in my own project, so thought I'd open a draft PR to see if there was interest in upstreaming it to Magnus.Additionally, this includes a
r_array::Iter
which I've found to faster than theEnumerator
returned byRArray::each
, but safer thanRArray::as_slice
. While there is the potential for unexpected behaviour if theRArray
is modified while being iterated, I think the worst-case scenario is skipping an item or iterating an item twice.