Skip to content

Proper iteration over Foundation types #304

Closed
@madsmtm

Description

@madsmtm

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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-frameworkAffects the framework crates and the translator for themI-unsoundA soundness holeenhancementNew feature or request

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions