-
Notifications
You must be signed in to change notification settings - Fork 548
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
feat(corelib): storage vectors iterators #6941
base: main
Are you sure you want to change the base?
Conversation
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.
Reviewable status: 0 of 4 files reviewed, 1 unresolved discussion (waiting on @julio4)
a discussion (no related file):
i'm not sure this is actually a good idea for the time being - as this sort of hides the fact that the result vector may be very very large.
the original code at least very specifically needs to specifiy the fact that iteration over a whole section of a vector is required.
Maybe iterating over a specific range of the storage is a bit more sensible? - so both (mostly) easy but still very explicit?
Previously, orizi wrote…
True. But I would argue that the cognitive difference between Maybe we could make size-bounded operations more clear and explicit in the code, for example by forcing iterator instanciation with explicit bound with new patterns like Thoughts? |
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.
Reviewed all commit messages.
Reviewable status: 0 of 4 files reviewed, 1 unresolved discussion (waiting on @julio4)
a discussion (no related file):
Previously, julio4 (Julio) wrote…
True. But I would argue that the cognitive difference between
0..vec.len()
andvec.into_iter()
is not that big.
Also iterators provide benefits not only loop syntax sugar, they enable interesting functional patterns and composition.Maybe we could make size-bounded operations more clear and explicit in the code, for example by forcing iterator instanciation with explicit bound with new patterns like
vec.into_iter().take(vec.len())
/vec.into_bounded_iter(vec.len())
?Thoughts?
do note that for x in vec
would work as well - whish isn't as clear as .into_vec
to most people probably.
the vec.into_bounded_iter(vec.len())
sound pretty close to the idea i suggested - vec.iter_range(4..89)
and vec.into_full_iter_range()
style thing.
So - not providing IntoIterator
just specific functions providing iterators sound much better to my mind.
Previously, orizi wrote…
Will do, this makes sense. |
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.
Reviewable status: 0 of 6 files reviewed, 1 unresolved discussion (waiting on @orizi)
a discussion (no related file):
Previously, julio4 (Julio) wrote…
Will do, this makes sense.
Done.
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.
Reviewed 2 of 6 files at r2, all commit messages.
Reviewable status: 2 of 6 files reviewed, 3 unresolved discussions (waiting on @julio4)
a discussion (no related file):
@gilbens-starkware 2nd eye.
corelib/src/iter/traits/iterator.cairo
line 22 at r2 (raw file):
/// Turn a collection of values into an iterator over a specific range. pub trait IntoIterRange<T, I> {
not sure this trait is required - why not add directly to the VecTrait
.
Code quote:
/// Turn a collection of values into an iterator over a specific range.
pub trait IntoIterRange<T, I> {
Previously, orizi wrote…
I thought it could be potentially implemented by other types. If you think it's useless I can move it in |
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.
Reviewable status: 2 of 6 files reviewed, 3 unresolved discussions (waiting on @julio4)
corelib/src/iter/traits/iterator.cairo
line 22 at r2 (raw file):
Previously, julio4 (Julio) wrote…
I thought it could be potentially implemented by other types. If you think it's useless I can move it in
VecTrait
, or a separateVecIterTrait
i think this is more relvant specifically for storage stuff - since in the usual case probably some combination of into_iter().skip(10).take(20)
makes more sense.
Previously, orizi wrote…
Should I move the IntoIterRange trait to the vec module and rename it to VecIntoIterRange, or should I extend VecTrait and MutableVecTrait directly with the new iterators methods? |
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.
Reviewable status: 2 of 6 files reviewed, 3 unresolved discussions (waiting on @julio4)
corelib/src/iter/traits/iterator.cairo
line 22 at r2 (raw file):
Previously, julio4 (Julio) wrote…
Should I move the IntoIterRange trait to the vec module and rename it to VecIntoIterRange, or should I extend VecTrait and MutableVecTrait directly with the new iterators methods?
oh - adding one trait and using it for both is probably nicer and opens more options later - so i like it all and all.
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.
Reviewable status: 2 of 6 files reviewed, 3 unresolved discussions (waiting on @gilbens-starkware, @julio4, and @orizi)
corelib/src/iter/traits/iterator.cairo
line 22 at r2 (raw file):
Previously, orizi wrote…
oh - adding one trait and using it for both is probably nicer and opens more options later - so i like it all and all.
Notice that this means that the impls will not be automatically found (is not near the trait nor the generic type) and thus it needs to be whereever it is used.
Previously, gilbens-starkware (Gil Ben-Shachar) wrote…
I also think it is nicer but yes the trait need to be used. Is there any pattern to "bring" automatically the trait in the scope of VecTrait? |
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.
Reviewable status: 2 of 6 files reviewed, 4 unresolved discussions (waiting on @julio4 and @orizi)
corelib/src/iter/traits/iterator.cairo
line 22 at r2 (raw file):
Previously, julio4 (Julio) wrote…
I also think it is nicer but yes the trait need to be used. Is there any pattern to "bring" automatically the trait in the scope of VecTrait?
Importing another trait is fine in this case. The problem is if we need to import the impls as well, but your code seems fine in this regard, does it work if you remove the imported impls from the tests (marked in another comment)?
crates/cairo-lang-starknet/cairo_level_tests/collections_test.cairo
line 4 at r2 (raw file):
MutableVecTrait, PathableMutableVecIntoIterRange, PathableVecIntoIterRange, StoragePathEntry, StoragePointerReadAccess, StoragePointerWriteAccess, };
Suggestion:
use starknet::storage::{
MutableVecTrait, StoragePathEntry,
StoragePointerReadAccess, StoragePointerWriteAccess,
};
use core::iter::IntoIterRange;
impl VecIntoIterRange<
T, impl VecTraitImpl: VecTrait<StoragePath<Vec<T>>>,
> of IntoIterRange<StoragePath<Vec<T>>> { //... } But then I have to edit impl PathableVecIntoIterRange<
T,
impl PathImpl: StorageAsPath<Vec<T>>,
impl VecTraitImpl: VecTrait<StoragePath<PathImpl::Value>>,
> of IntoIterRange<Vec<T>> {
type IntoIter = VecIter<StoragePath<PathImpl::Value>, VecTraitImpl>;
#[inline]
fn into_iter_range(self: Vec<T>, range: Range<u64>) -> Self::IntoIter {
VecIntoIterRange::<T, VecTraitImpl>::into_iter_range(self.as_path(), range)
}
#[inline]
fn into_iter_full_range(self: Vec<T>) -> Self::IntoIter {
VecIntoIterRange::<T, VecTraitImpl>::into_iter_full_range(self.as_path())
}
} In the end, it was more easy to just duplicate the implementation and not delegate, that's what I did and updated the PR. But if there's any way to correctly do trait bounds I would be curious to know! |
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.
Reviewable status: 2 of 6 files reviewed, 4 unresolved discussions (waiting on @gilbens-starkware and @orizi)
crates/cairo-lang-starknet/cairo_level_tests/collections_test.cairo
line 4 at r2 (raw file):
MutableVecTrait, PathableMutableVecIntoIterRange, PathableVecIntoIterRange, StoragePathEntry, StoragePointerReadAccess, StoragePointerWriteAccess, };
See other comment, :done:
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.
Reviewed 3 of 5 files at r3, all commit messages.
Reviewable status: 4 of 6 files reviewed, 3 unresolved discussions (waiting on @gilbens-starkware and @julio4)
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.
Reviewable status: 2 of 6 files reviewed, 2 unresolved discussions (waiting on @julio4 and @orizi)
corelib/src/iter/traits/iterator.cairo
line 22 at r2 (raw file):
Previously, julio4 (Julio) wrote…
When not explicitly importing impls there was an error of duplicated impl.
By makingVecIntoIter
impl less generic it works correctly as it can resolved the correct impl:impl VecIntoIterRange< T, impl VecTraitImpl: VecTrait<StoragePath<Vec<T>>>, > of IntoIterRange<StoragePath<Vec<T>>> { //... }But then I have to edit
PathableVecIntoIterRange
to be able to delegate, and I didn't found a way to correctly boundVec<T>
to theStorageAsPath<Vec<T>>::Value
. I tried something like this but it's not working:impl PathableVecIntoIterRange< T, impl PathImpl: StorageAsPath<Vec<T>>, impl VecTraitImpl: VecTrait<StoragePath<PathImpl::Value>>, > of IntoIterRange<Vec<T>> { type IntoIter = VecIter<StoragePath<PathImpl::Value>, VecTraitImpl>; #[inline] fn into_iter_range(self: Vec<T>, range: Range<u64>) -> Self::IntoIter { VecIntoIterRange::<T, VecTraitImpl>::into_iter_range(self.as_path(), range) } #[inline] fn into_iter_full_range(self: Vec<T>) -> Self::IntoIter { VecIntoIterRange::<T, VecTraitImpl>::into_iter_full_range(self.as_path()) } }In the end, it was more easy to just duplicate the implementation and not delegate, that's what I did and updated the PR. But if there's any way to correctly do trait bounds I would be curious to know!
That's good.
But in order to implement this trait for future storage object i'd move this trait to storage.cairo
and import the impls there too (in order for them to be automatically found).
82d27d2
to
c216934
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.
Reviewable status: 0 of 6 files reviewed, 2 unresolved discussions (waiting on @gilbens-starkware and @orizi)
corelib/src/iter/traits/iterator.cairo
line 22 at r2 (raw file):
Previously, gilbens-starkware (Gil Ben-Shachar) wrote…
That's good.
But in order to implement this trait for future storage object i'd move this trait tostorage.cairo
and import the impls there too (in order for them to be automatically found).
I moved IntoIterRange to starknet::storage
c216934
to
23c32ab
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.
Reviewable status: 0 of 6 files reviewed, 1 unresolved discussion (waiting on @julio4 and @orizi)
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.
Reviewed 3 of 5 files at r5, 1 of 1 files at r6, all commit messages.
Reviewable status: 4 of 6 files reviewed, 1 unresolved discussion (waiting on @gilbens-starkware and @julio4)
corelib/src/starknet/storage/vec.cairo
line 445 at r6 (raw file):
fn into_iter_full_range(self: T) -> Self::IntoIter { let vec = self.as_path(); VecIter { current_index: (0..vec.len()).into_iter(), vec }
we currently range check every access - so no need for the length read.
alternatively - check ranges here - and use update
instead of get
in the iterator implementation.
(probably better)
Suggestion:
VecIter { current_index: (0..core::num::traits::Bounded::MAX).into_iter(), vec }
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.
Reviewable status: 4 of 6 files reviewed, 1 unresolved discussion (waiting on @gilbens-starkware and @orizi)
corelib/src/starknet/storage/vec.cairo
line 445 at r6 (raw file):
Previously, orizi wrote…
we currently range check every access - so no need for the length read.
alternatively - check ranges here - and use
update
instead ofget
in the iterator implementation.
(probably better)
I'm not sure I understand what you mean by using update
?
get
return None
on out of bound, so I agree we don't need to actually call len
there.
Then, is there even a need for a range? We could simply use cur: u64
, to not have to create a RangeIterator
struct.
Also range end are exclusive, so technically it can only iterate until core::num::traits::Bounded::MAX - 1
if not mistaken, not a big issue but still maybe not exactly the specification.
23c32ab
to
8c1a523
Compare
Add new iterator trait
IntoIterRange<T>
in the vec module to turn a collection of values into an iterator over a specific range, withinto_iter_range(range: Range<u64>)
orinto_iter_full_range()
.Add
IntoIterRange
implementations for storage vectors to enable iterator over vectors in storage.VecIntoIterRange
andMutableVecIntoIterRange
for direct iteration overStoragePath<Vec<T>>
andStoragePath<Mutable<Vec<T>>>
PathableVecIntoIterRange
andPathableMutableVecIntoIterRange
for iteration over types implementingStorageAsPath
into storage vectors (such as Vecs in Starknet contract storage structs)You need to bring the trait in the current scope:
Before:
After:
Examples