The way we do NSEnumerator and NSFastEnumeration right now is a mess!
Ideally, I don't think NSEnumerator should have a lifetime (it would allow it to be treated normally in icrate). This means that any method that creates an NSEnumerator is inherently unsafe, but instead I think we should just create proper methods and Iterator wrappers where applicable.
Rough sketches below. Note that the iterator must sometimes hold a reference to the array, since otherwise the array could be modified. Luckily the returned items can have the same lifetime as the array (instead of e.g. the same lifetime as the iterator).
impl<T, O> NSArray<T, O> {
fn iter<'a>(&'a self) -> impl Iterator<&'a T> + 'a;
}
impl<T> NSArray<T, Owned> {
fn iter_mut<'a>(&'a mut self) -> impl Iterator<&'a mut T> + 'a;
}
impl<T> NSArray<T, Shared> {
// While the items are immutable, the array may not be, so we still need to bind the lifetime of the iterator to the array
fn iter_retained<'a>(&'a self) -> impl Iterator<Id<T, Shared>> + '_;
// But here we know that the array is immutable, so with this design we wouldn't need to
fn iter_retained2(this: &Id<Self, Shared>) -> impl Iterator<Id<T, Shared>>;
}
impl<T, O> NSArray<T, O> {
fn into_iter(this: Id<Self, O>) -> impl Iterator<Id<T, O>>;
}
// Or perhaps ideally:
// impl<T> NSArray<T, Shared> {
// fn into_iter(this: Id<Self, Owned>) -> impl Iterator<Id<T, Shared>>;
// fn into_iter(this: Id<Self, Shared>) -> impl Iterator<Id<T, Shared>>;
// }
// impl<T> NSArray<T, Owned> {
// fn into_iter(this: Id<Self, Owned>) -> impl Iterator<Id<T, Owned>>;
// fn into_iter(this: Id<Self, Shared>) -> impl Iterator<Id<T, Shared>>;
// }
impl<K, V, KO, VO> NSDictionary<K, V, KO, VO> {
fn iter_keys<'a>(&'a self) -> impl Iterator<&'a K> + 'a;
fn iter_values<'a>(&'a self) -> impl Iterator<&'a V> + 'a;
fn iter<'a>(&'a self) -> impl Iterator<(&'a K, &'a V)> + 'a {
std::iter::zip(self.iter_keys(), self.iter_values())
}
}
impl<K, V, VO> NSDictionary<K, V, Owned, VO> {
fn iter_keys_mut<'a>(&'a mut self) -> impl Iterator<&'a mut K> + 'a;
}
impl<K, V, KO> NSDictionary<K, V, KO, Owned> {
fn iter_values_mut<'a>(&'a mut self) -> impl Iterator<&'a mut V> + 'a;
}
impl<K, V, VO> NSDictionary<K, V, Shared, VO> {
fn iter_keys_retained<'a>(&'a self) -> impl Iterator<Id<K, Shared>> + 'a;
fn iter_keys_retained2(this: &Id<Self, Shared>) -> impl Iterator<Id<K, Shared>>;
}
// And so on...
// Unsure if the following is possible?
impl<K, V> NSDictionary<K, V, Owned, Owned> {
fn iter_mut<'a>(&'a mut self) -> impl Iterator<(&'a mut K, &'a mut V)> + 'a;
}
// And maybe more vairants that return
// - (&'a K, &'a mut V)
// - (&'a mut K, &'a V)
// - (&'a mut K, Id<V, Shared>)
// - ...
See also #29 (comment)
The way we do
NSEnumeratorandNSFastEnumerationright now is a mess!Ideally, I don't think
NSEnumeratorshould have a lifetime (it would allow it to be treated normally inicrate). This means that any method that creates anNSEnumeratoris inherentlyunsafe, but instead I think we should just create proper methods andIteratorwrappers where applicable.Rough sketches below. Note that the iterator must sometimes hold a reference to the array, since otherwise the array could be modified. Luckily the returned items can have the same lifetime as the array (instead of e.g. the same lifetime as the iterator).
See also #29 (comment)