Skip to content

Commit aa2d5b9

Browse files
committed
Add IntoIterator implementations for Id
1 parent 890edc1 commit aa2d5b9

File tree

5 files changed

+216
-46
lines changed

5 files changed

+216
-46
lines changed

crates/objc2/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
5252
`extern_protocol!` and `extern_methods!`.
5353
* Allow arbitary expressions in `const NAME` in `extern_class!`,
5454
`extern_protocol!` and `declare_class!`.
55+
* Added `rc::IdIntoIterator` helper trait and forwarding `IntoIterator`
56+
implementations for `rc::Id`.
5557

5658
### Changed
5759
* **BREAKING**: `objc2::rc::AutoreleasePool` is now a zero-sized `Copy` type
@@ -102,6 +104,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
102104
* **BREAKING**: Removed `impl<T> TryFrom<WeakId<T>> for Id<T>` impl since it
103105
did not have a proper error type, making it less useful than just using
104106
`WeakId::load`.
107+
* **BREAKING**: Removed forwarding `Iterator` implementation for `Id`, since
108+
it conflicts with the `IntoIterator` implementation that it now has instead.
105109

106110

107111
## 0.3.0-beta.5 - 2023-02-07

crates/objc2/src/rc/id.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,27 @@ use crate::{ffi, ClassType, Message};
4444
/// [`Box`]: alloc::boxed::Box
4545
///
4646
///
47+
/// # Forwarding implementations
48+
///
49+
/// Since `Id<T>` is a smart pointer, it naturally [`Deref`]s to `T`, and
50+
/// similarly implements [`DerefMut`] when mutable.
51+
///
52+
/// On top of this, it also forwards the implementation of a bunch of standard
53+
/// library traits such as [`PartialEq`], [`AsRef`], and so on, so that it
54+
/// becomes easy to use e.g. `Id<NSString>` as if it was just an `NSString`.
55+
/// (Having just `NSString` is not possible since Objective-C objects cannot
56+
/// live on the stack, but instead must reside on the heap).
57+
///
58+
/// Note that because of current limitations in the Rust trait system, some
59+
/// traits like [`Default`] and [`IntoIterator`] are not directly
60+
/// implementable on `NSString`; for that ues-case, we provide the
61+
/// [`DefaultId`] and [`IdIntoIterator`] traits, which make the aforementioned
62+
/// traits available on `Id`.
63+
///
64+
/// [`IdIntoIterator`]: crate::rc::IdIntoIterator
65+
/// [`DefaultId`]: crate::rc::DefaultId
66+
///
67+
///
4768
/// # Memory layout
4869
///
4970
/// This is guaranteed to have the same size and alignment as a pointer to the

crates/objc2/src/rc/id_forwarding_impls.rs

Lines changed: 0 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ use core::cmp::Ordering;
1313
use core::fmt;
1414
use core::future::Future;
1515
use core::hash;
16-
use core::iter::FusedIterator;
1716
use core::ops::{Deref, DerefMut};
1817
use core::pin::Pin;
1918
use core::task::{Context, Poll};
@@ -131,49 +130,6 @@ impl<T: fmt::Debug + ?Sized> fmt::Debug for Id<T> {
131130
}
132131
}
133132

134-
impl<I: Iterator + ?Sized + IsMutable> Iterator for Id<I> {
135-
type Item = I::Item;
136-
fn next(&mut self) -> Option<I::Item> {
137-
(**self).next()
138-
}
139-
fn size_hint(&self) -> (usize, Option<usize>) {
140-
(**self).size_hint()
141-
}
142-
fn nth(&mut self, n: usize) -> Option<I::Item> {
143-
(**self).nth(n)
144-
}
145-
}
146-
147-
impl<I: DoubleEndedIterator + ?Sized + IsMutable> DoubleEndedIterator for Id<I> {
148-
fn next_back(&mut self) -> Option<I::Item> {
149-
(**self).next_back()
150-
}
151-
fn nth_back(&mut self, n: usize) -> Option<I::Item> {
152-
(**self).nth_back(n)
153-
}
154-
}
155-
156-
impl<I: ExactSizeIterator + ?Sized + IsMutable> ExactSizeIterator for Id<I> {
157-
fn len(&self) -> usize {
158-
(**self).len()
159-
}
160-
}
161-
162-
impl<I: FusedIterator + ?Sized + IsMutable> FusedIterator for Id<I> {}
163-
164-
// TODO: Consider this impl
165-
// impl<'a, T> IntoIterator for &'a Id<T>
166-
// where
167-
// &'a T: IntoIterator,
168-
// {
169-
// type Item = <&'a T as IntoIterator>::Item;
170-
// type IntoIter = <&'a T as IntoIterator>::IntoIter;
171-
//
172-
// fn into_iter(self) -> Self::IntoIter {
173-
// (**self).into_iter()
174-
// }
175-
// }
176-
177133
impl<T: ?Sized> borrow::Borrow<T> for Id<T> {
178134
fn borrow(&self) -> &T {
179135
Deref::deref(self)

crates/objc2/src/rc/id_traits.rs

Lines changed: 190 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Helper traits for Id.
22
33
use super::Id;
4-
use crate::mutability::IsAllocableAnyThread;
4+
use crate::mutability::{IsAllocableAnyThread, IsMutable};
55

66
/// Helper trait to implement [`Default`] on [`Id`].
77
// TODO: Maybe make this `unsafe` and provide a default implementation?
@@ -19,3 +19,192 @@ impl<T: ?Sized + DefaultId> Default for Id<T> {
1919
T::default_id()
2020
}
2121
}
22+
23+
/// Helper trait to implement [`IntoIterator`] on [`Id`].
24+
///
25+
/// This should be implemented in exactly the same fashion as if you were
26+
/// implementing `IntoIterator` for your type normally.
27+
//
28+
// Note that [`Box<T>` gets to cheat with regards moves][box-move], so
29+
// `boxed.into_iter()` is possible, while `id.into_iter()` is not possible
30+
// without this helper trait.
31+
//
32+
// [box-move]: https://doc.rust-lang.org/reference/expressions.html#moved-and-copied-types
33+
pub trait IdIntoIterator {
34+
/// The type of the elements being iterated over.
35+
type Item;
36+
37+
/// Which kind of iterator are we turning this into?
38+
type IntoIter: Iterator<Item = Self::Item>;
39+
40+
/// Creates an iterator from an [`Id`].
41+
///
42+
/// You would normally not call this function directly; instead, you'd
43+
/// just call [`into_iter`](IntoIterator::into_iter) on an [`Id`].
44+
fn id_into_iter(this: Id<Self>) -> Self::IntoIter;
45+
}
46+
47+
// Note: These `IntoIterator` implementations conflict with an `Iterator`
48+
// implementation for `Id`.
49+
//
50+
// For our case however (in contrast with `Box`), that is the better tradeoff,
51+
// which I will show with an example:
52+
//
53+
// ```
54+
// let xs = Box::new(vec![]);
55+
// for x in &xs { // Doesn't compile, `&Box` doesn't implement `IntoIterator`
56+
// // ...
57+
// }
58+
// ```
59+
//
60+
// Here, you're expected to write `xs.iter()` or `&**xs` instead, which is
61+
// fairly acceptable, since usually people don't wrap things in boxes so much;
62+
// but in Objective-C, _everything_ is wrapped in an `Id`, and hence we should
63+
// attempt to make that common case easier:
64+
//
65+
// ```
66+
// let obj = NSArray::new(); // `Id<NSArray<_>>`
67+
// for item in &obj { // Should compile
68+
// // ...
69+
// }
70+
// ```
71+
//
72+
// The loss of the `Iterator` impl is a bit unfortunate, but not a big deal,
73+
// since there is only one iterator in Objective-C anyhow, `NSEnumerator`, and
74+
// for that we can make other abstractions instead.
75+
impl<T: ?Sized + IdIntoIterator> IntoIterator for Id<T> {
76+
type Item = <T as IdIntoIterator>::Item;
77+
type IntoIter = <T as IdIntoIterator>::IntoIter;
78+
79+
#[inline]
80+
fn into_iter(self) -> Self::IntoIter {
81+
T::id_into_iter(self)
82+
}
83+
}
84+
85+
impl<'a, T: ?Sized> IntoIterator for &'a Id<T>
86+
where
87+
&'a T: IntoIterator,
88+
{
89+
type Item = <&'a T as IntoIterator>::Item;
90+
type IntoIter = <&'a T as IntoIterator>::IntoIter;
91+
92+
#[inline]
93+
fn into_iter(self) -> Self::IntoIter {
94+
(&**self).into_iter()
95+
}
96+
}
97+
98+
impl<'a, T: ?Sized + IsMutable> IntoIterator for &'a mut Id<T>
99+
where
100+
&'a mut T: IntoIterator,
101+
{
102+
type Item = <&'a mut T as IntoIterator>::Item;
103+
type IntoIter = <&'a mut T as IntoIterator>::IntoIter;
104+
105+
#[inline]
106+
fn into_iter(self) -> Self::IntoIter {
107+
(&mut **self).into_iter()
108+
}
109+
}
110+
111+
112+
#[cfg(test)]
113+
mod tests {
114+
use super::*;
115+
use crate::mutability::Mutable;
116+
use crate::runtime::NSObject;
117+
use crate::{declare_class, msg_send_id, ClassType};
118+
119+
declare_class!(
120+
#[derive(PartialEq, Eq, Hash, Debug)]
121+
struct Collection;
122+
123+
unsafe impl ClassType for Collection {
124+
type Super = NSObject;
125+
type Mutability = Mutable;
126+
const NAME: &'static str = "MyCustomCollection";
127+
}
128+
);
129+
130+
impl DefaultId for Collection {
131+
fn default_id() -> Id<Self> {
132+
unsafe { msg_send_id![Collection::class(), new] }
133+
}
134+
}
135+
136+
struct Iter<'a>(&'a Collection);
137+
138+
impl<'a> Iterator for Iter<'a> {
139+
type Item = &'a NSObject;
140+
fn next(&mut self) -> Option<Self::Item> {
141+
None
142+
}
143+
}
144+
145+
impl<'a> IntoIterator for &'a Collection {
146+
type Item = &'a NSObject;
147+
type IntoIter = Iter<'a>;
148+
149+
fn into_iter(self) -> Self::IntoIter {
150+
Iter(self)
151+
}
152+
}
153+
154+
struct IterMut<'a>(&'a mut Collection);
155+
156+
impl<'a> Iterator for IterMut<'a> {
157+
type Item = &'a mut NSObject;
158+
fn next(&mut self) -> Option<Self::Item> {
159+
None
160+
}
161+
}
162+
163+
impl<'a> IntoIterator for &'a mut Collection {
164+
// Usually only valid if a mutable object is stored in the collection.
165+
type Item = &'a mut NSObject;
166+
type IntoIter = IterMut<'a>;
167+
168+
fn into_iter(self) -> Self::IntoIter {
169+
IterMut(self)
170+
}
171+
}
172+
173+
struct IntoIter(Id<Collection>);
174+
175+
impl Iterator for IntoIter {
176+
type Item = Id<NSObject>;
177+
fn next(&mut self) -> Option<Self::Item> {
178+
None
179+
}
180+
}
181+
182+
impl<'a> IntoIterator for Id<Collection> {
183+
type Item = Id<NSObject>;
184+
type IntoIter = IntoIter;
185+
186+
fn into_iter(self) -> Self::IntoIter {
187+
IntoIter(self)
188+
}
189+
}
190+
191+
#[test]
192+
fn test_default() {
193+
let obj1: Id<Collection> = Default::default();
194+
let obj2 = Collection::default_id();
195+
assert_ne!(obj1, obj2);
196+
}
197+
198+
#[test]
199+
fn test_into_iter() {
200+
let mut obj: Id<Collection> = Default::default();
201+
202+
for _ in &*obj {}
203+
for _ in &obj {}
204+
205+
for _ in &mut *obj {}
206+
for _ in &mut obj {}
207+
208+
for _ in obj {}
209+
}
210+
}

crates/objc2/src/rc/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,6 @@ pub use self::autorelease::{
6262
autoreleasepool, autoreleasepool_leaking, AutoreleasePool, AutoreleaseSafe,
6363
};
6464
pub use self::id::Id;
65-
pub use self::id_traits::DefaultId;
65+
pub use self::id_traits::{DefaultId, IdIntoIterator};
6666
pub use self::test_object::{__RcTestObject, __ThreadTestData};
6767
pub use self::weak_id::WeakId;

0 commit comments

Comments
 (0)