objc2_foundation/
dictionary.rs

1//! Utilities for the `NSDictionary` and `NSMutableDictionary` classes.
2#[cfg(feature = "alloc")]
3use alloc::vec::Vec;
4use core::fmt;
5use core::mem;
6use core::ptr::NonNull;
7use objc2::msg_send;
8
9use objc2::rc::Retained;
10#[cfg(feature = "NSObject")]
11use objc2::runtime::ProtocolObject;
12#[cfg(feature = "NSObject")]
13use objc2::AnyThread;
14use objc2::Message;
15
16#[cfg(feature = "NSEnumerator")]
17use crate::iter;
18#[cfg(feature = "NSObject")]
19use crate::{util, CopyingHelper, NSCopying};
20use crate::{NSDictionary, NSMutableDictionary};
21
22#[cfg(feature = "NSObject")]
23fn keys_to_ptr<CopiedKey>(keys: &[&CopiedKey]) -> *mut NonNull<ProtocolObject<dyn NSCopying>>
24where
25    CopiedKey: Message + NSCopying,
26{
27    let keys: *mut NonNull<CopiedKey> = util::ref_ptr_cast_const(keys.as_ptr());
28    // SAFETY: `CopiedKey` is `Message + NSCopying`, and is therefore safe to cast to
29    // `ProtocolObject<dyn NSCopying>`.
30    let keys: *mut NonNull<ProtocolObject<dyn NSCopying>> = keys.cast();
31    keys
32}
33
34/// Convenience creation methods.
35impl<KeyType: Message, ObjectType: Message> NSDictionary<KeyType, ObjectType> {
36    /// Create a new dictionary from a slice of keys, and a slice of objects.
37    ///
38    /// This is a safe interface to `initWithObjects:forKeys:count:`.
39    ///
40    /// # Panics
41    ///
42    /// Panics if the slices have different lengths, as this is likely a
43    /// programmer error.
44    ///
45    /// # Example
46    ///
47    /// ```
48    /// use objc2_foundation::{NSDictionary, ns_string};
49    ///
50    /// let dict = NSDictionary::from_slices(
51    ///     &[ns_string!("key1"), ns_string!("key2"), ns_string!("key3")],
52    ///     &[ns_string!("value1"), ns_string!("value2"), ns_string!("value3")],
53    /// );
54    ///
55    /// assert_eq!(&*dict.objectForKey(ns_string!("key2")).unwrap(), ns_string!("value2"));
56    /// ```
57    #[cfg(feature = "NSObject")]
58    pub fn from_slices<CopiedKey>(keys: &[&CopiedKey], objects: &[&ObjectType]) -> Retained<Self>
59    where
60        // The dictionary copies its keys, which is why we require `NSCopying`
61        // and use `CopyingHelper` on all input data - we want to ensure that
62        // the type-system knows that it's not actually e.g. `NSMutableString`
63        // that is being stored, but instead `NSString`.
64        CopiedKey: Message + NSCopying + CopyingHelper<Result = KeyType>,
65    {
66        // Ensure that we don't read too far into one of the buffers.
67        assert_eq!(
68            keys.len(),
69            objects.len(),
70            "key slice and object slice should have the same length",
71        );
72        let count = keys.len();
73
74        let keys = keys_to_ptr(keys);
75        let objects = util::ref_ptr_cast_const(objects.as_ptr());
76
77        // SAFETY:
78        // - All types that are `Message` use interior mutability, and the
79        //   dictionary extends the lifetime of them internally by retaining
80        //   them.
81        //
82        // - The pointers are valid until the method has finished executing,
83        //   at which point the dictionary will have created its own internal
84        //   storage for holding the keys and objects.
85        //
86        // - The length is lower than or equal to the length of the two
87        //   pointers.
88        //
89        // - While recommended against in the below link, the key _can_ be
90        //   mutated, it'll "just" corrupt the collection's invariants (but
91        //   won't cause undefined behaviour).
92        //
93        //   This is tested via fuzzing in `collection_interior_mut.rs`.
94        //
95        //   <https://developer.apple.com/library/archive/documentation/General/Conceptual/CocoaEncyclopedia/ObjectMutability/ObjectMutability.html#//apple_ref/doc/uid/TP40010810-CH5-SW69>
96        unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), objects, keys, count) }
97    }
98
99    #[cfg(feature = "NSObject")]
100    pub fn from_retained_objects<CopiedKey>(
101        keys: &[&CopiedKey],
102        objects: &[Retained<ObjectType>],
103    ) -> Retained<Self>
104    where
105        CopiedKey: Message + NSCopying + CopyingHelper<Result = KeyType>,
106    {
107        // Ensure that we don't read too far into one of the buffers.
108        assert_eq!(
109            keys.len(),
110            objects.len(),
111            "key slice and object slice should have the same length",
112        );
113        let count = keys.len();
114
115        let keys = keys_to_ptr(keys);
116        let objects = util::retained_ptr_cast_const(objects.as_ptr());
117
118        // SAFETY: Same as `from_slices`.
119        unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), objects, keys, count) }
120    }
121}
122
123/// Convenience creation methods.
124impl<KeyType: Message, ObjectType: Message> NSMutableDictionary<KeyType, ObjectType> {
125    #[cfg(feature = "NSObject")]
126    pub fn from_slices<CopiedKey>(keys: &[&CopiedKey], objects: &[&ObjectType]) -> Retained<Self>
127    where
128        CopiedKey: Message + NSCopying + CopyingHelper<Result = KeyType>,
129    {
130        // Ensure that we don't read too far into one of the buffers.
131        assert_eq!(
132            keys.len(),
133            objects.len(),
134            "key slice and object slice should have the same length",
135        );
136        let count = keys.len();
137
138        let keys = keys_to_ptr(keys);
139        let objects = util::ref_ptr_cast_const(objects.as_ptr());
140
141        // SAFETY: Same as `NSDictionary::from_slices`.
142        unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), objects, keys, count) }
143    }
144
145    #[cfg(feature = "NSObject")]
146    pub fn from_retained_objects<CopiedKey>(
147        keys: &[&CopiedKey],
148        objects: &[Retained<ObjectType>],
149    ) -> Retained<Self>
150    where
151        CopiedKey: Message + NSCopying + CopyingHelper<Result = KeyType>,
152    {
153        // Ensure that we don't read too far into one of the buffers.
154        assert_eq!(
155            keys.len(),
156            objects.len(),
157            "key slice and object slice should have the same length",
158        );
159        let count = keys.len();
160
161        let keys = keys_to_ptr(keys);
162        let objects = util::retained_ptr_cast_const(objects.as_ptr());
163
164        // SAFETY: Same as `NSDictionary::from_retained_objects`.
165        unsafe { Self::initWithObjects_forKeys_count(Self::alloc(), objects, keys, count) }
166    }
167}
168
169// Note: We'd like to make getter methods take `KeyType: Borrow<KeyType>`
170// like `std::collections::HashMap`, so that e.g. `NSDictionary<NSString, V>`
171// could take a `&NSObject` as input, and still make that work since
172// `NSString` borrows to `NSObject`.
173//
174// But we can't really, at least not with extra `unsafe` / an extra trait,
175// since we don't control how the comparisons happen.
176//
177// The most useful alternative would probably be to take `impl AsRef<KeyType>`, but
178// objc2 classes deref to their superclass anyhow, so let's just use a simple,
179// normal reference.
180
181/// Direct, unsafe object accessors.
182///
183/// Foundation's collection types store their items in such a way that they
184/// can give out references to their data without having to autorelease it
185/// first, see [the docs][collections-own].
186///
187/// This means that we can more efficiently access the dictionary's keys and
188/// objects, but _only_ if the dictionary isn't mutated via e.g.
189/// `NSMutableDictionary` methods while doing so - otherwise, we might end up
190/// accessing a deallocated object.
191///
192/// [collections-own]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmPractical.html#//apple_ref/doc/uid/TP40004447-SW12
193impl<KeyType: Message, ObjectType: Message> NSDictionary<KeyType, ObjectType> {
194    /// Get a direct reference to the object corresponding to the key.
195    ///
196    /// Consider using the [`objectForKey`](Self::objectForKey) method
197    /// instead, unless you're seeing performance issues from the retaining.
198    ///
199    /// # Safety
200    ///
201    /// The dictionary must not be mutated while the reference is live.
202    #[doc(alias = "objectForKey:")]
203    #[inline]
204    pub unsafe fn objectForKey_unchecked(&self, key: &KeyType) -> Option<&ObjectType> {
205        unsafe { msg_send![self, objectForKey: key] }
206    }
207
208    /// Two vectors containing direct references to respectively the
209    /// dictionary's keys and objects.
210    ///
211    /// Consider using the [`to_vecs`](Self::to_vecs) method instead, unless
212    /// you're seeing performance issues from the retaining.
213    ///
214    /// # Safety
215    ///
216    /// The dictionary must not be mutated while the returned references are
217    /// alive.
218    #[doc(alias = "getObjects:andKeys:")]
219    #[cfg(feature = "alloc")]
220    pub unsafe fn to_vecs_unchecked(&self) -> (Vec<&KeyType>, Vec<&ObjectType>) {
221        let len = self.len();
222        let mut keys = Vec::with_capacity(len);
223        let mut objs = Vec::with_capacity(len);
224
225        // SAFETY: The pointers are valid.
226        unsafe {
227            // No reason to use `getObjects:andKeys:count:`, the dictionary is
228            // not thread safe, so we know it won't change within this scope.
229            #[allow(deprecated)]
230            self.getObjects_andKeys(objs.as_mut_ptr(), keys.as_mut_ptr());
231        }
232
233        // SAFETY: The vecs were just initialized by `getObjects:andKeys:`.
234        unsafe {
235            keys.set_len(len);
236            objs.set_len(len);
237        }
238
239        // SAFETY: `NonNull<T>` and `&T` have the same memory layout, and the
240        // lifetime is upheld by the caller.
241        unsafe {
242            (
243                mem::transmute::<Vec<NonNull<KeyType>>, Vec<&KeyType>>(keys),
244                mem::transmute::<Vec<NonNull<ObjectType>>, Vec<&ObjectType>>(objs),
245            )
246        }
247    }
248
249    /// Iterate over the dictionary's keys without retaining them.
250    ///
251    /// Consider using the [`keys`](Self::keys) method instead, unless you're
252    /// seeing performance issues from the retaining.
253    ///
254    /// # Safety
255    ///
256    /// The dictionary must not be mutated for the lifetime of the iterator,
257    /// or the elements it returns.
258    #[cfg(feature = "NSEnumerator")]
259    #[doc(alias = "keyEnumerator")]
260    #[inline]
261    pub unsafe fn keys_unchecked(&self) -> KeysUnchecked<'_, KeyType, ObjectType> {
262        KeysUnchecked(iter::IterUnchecked::new(self))
263    }
264
265    /// Iterate over the dictionary's objects / values without retaining them.
266    ///
267    /// Consider using the [`objects`](Self::objects) method instead, unless
268    /// you're seeing performance issues from the retaining.
269    ///
270    /// # Safety
271    ///
272    /// The dictionary must not be mutated for the lifetime of the iterator,
273    /// or the elements it returns.
274    #[cfg(feature = "NSEnumerator")]
275    #[doc(alias = "objectEnumerator")]
276    #[inline]
277    pub unsafe fn objects_unchecked(&self) -> ObjectsUnchecked<'_, KeyType, ObjectType> {
278        // SAFETY: Avoiding mutation is upheld by caller.
279        let enumerator = unsafe { self.objectEnumerator() };
280        // SAFETY: The enumerator came from the dictionary.
281        ObjectsUnchecked(unsafe { iter::IterUncheckedWithBackingEnum::new(self, enumerator) })
282    }
283}
284
285/// Various accessor methods.
286impl<KeyType: Message, ObjectType: Message> NSDictionary<KeyType, ObjectType> {
287    /// The amount of elements in the dictionary.
288    #[doc(alias = "count")]
289    #[inline]
290    pub fn len(&self) -> usize {
291        self.count()
292    }
293
294    /// Whether the dictionary is empty or not.
295    #[inline]
296    pub fn is_empty(&self) -> bool {
297        self.len() == 0
298    }
299
300    /// Two vectors containing respectively the dictionary's keys and objects.
301    ///
302    /// # Example
303    ///
304    /// Iterate over the keys and values of the dictionary.
305    ///
306    /// ```
307    /// use objc2_foundation::{NSDictionary, ns_string};
308    ///
309    /// let dict = NSDictionary::from_slices(
310    ///     &[ns_string!("a"), ns_string!("b")],
311    ///     &[ns_string!("a"), ns_string!("b")],
312    /// );
313    /// let (keys, objects) = dict.to_vecs();
314    /// for (key, obj) in keys.into_iter().zip(objects) {
315    ///     assert_eq!(key, obj);
316    /// }
317    /// ```
318    #[doc(alias = "getObjects:")]
319    #[cfg(feature = "alloc")]
320    pub fn to_vecs(&self) -> (Vec<Retained<KeyType>>, Vec<Retained<ObjectType>>) {
321        // SAFETY: We retain the elements below, so that we know that the
322        // dictionary isn't mutated while they are alive.
323        let (keys, objects) = unsafe { self.to_vecs_unchecked() };
324        (
325            keys.into_iter().map(KeyType::retain).collect(),
326            objects.into_iter().map(ObjectType::retain).collect(),
327        )
328    }
329
330    /// Iterate over the dictionary's keys.
331    #[cfg(feature = "NSEnumerator")]
332    #[doc(alias = "keyEnumerator")]
333    #[inline]
334    pub fn keys(&self) -> Keys<'_, KeyType, ObjectType> {
335        Keys(iter::Iter::new(self))
336    }
337
338    /// Iterate over the dictionary's objects / values.
339    ///
340    /// # Examples
341    ///
342    #[cfg_attr(feature = "NSString", doc = "```")]
343    #[cfg_attr(not(feature = "NSString"), doc = "```ignore")]
344    /// use objc2_foundation::{ns_string, NSMutableDictionary, NSString};
345    ///
346    /// let dict = NSMutableDictionary::new();
347    /// dict.insert(ns_string!("key1"), ns_string!("value1"));
348    /// dict.insert(ns_string!("key2"), ns_string!("value2"));
349    /// for obj in dict.objects() {
350    ///     assert!(obj.hasPrefix(ns_string!("value")));
351    /// }
352    /// ```
353    #[cfg(feature = "NSEnumerator")]
354    #[doc(alias = "objectEnumerator")]
355    #[inline]
356    pub fn objects(&self) -> Objects<'_, KeyType, ObjectType> {
357        // SAFETY: The iterator checks for mutation while enumerating.
358        let enumerator = unsafe { self.objectEnumerator() };
359        // SAFETY: The enumerator came from the dictionary.
360        Objects(unsafe { iter::IterWithBackingEnum::new(self, enumerator) })
361    }
362}
363
364/// Convenience mutation methods.
365impl<KeyType: Message, ObjectType: Message> NSMutableDictionary<KeyType, ObjectType> {
366    /// Inserts a key-value pair into the dictionary.
367    ///
368    /// If the dictionary did not have this key present, the value is
369    /// inserted. If the dictionary already had this key present, the value
370    /// and the key is updated.
371    ///
372    /// # Examples
373    ///
374    /// ```
375    /// use objc2_foundation::{ns_string, NSMutableDictionary, NSObject};
376    ///
377    /// let dict = NSMutableDictionary::new();
378    /// dict.insert(ns_string!("key"), &*NSObject::new());
379    /// ```
380    #[cfg(feature = "NSObject")]
381    #[doc(alias = "setObject:forKey:")]
382    #[inline]
383    pub fn insert<CopiedKey>(&self, key: &CopiedKey, object: &ObjectType)
384    where
385        CopiedKey: Message + NSCopying + CopyingHelper<Result = KeyType>,
386    {
387        let key = ProtocolObject::from_ref(key);
388        // SAFETY: The key is copied, and then has the correct type `KeyType`.
389        unsafe { self.setObject_forKey(object, key) };
390    }
391}
392
393#[cfg(feature = "NSEnumerator")]
394unsafe impl<KeyType: Message, ObjectType: Message> iter::FastEnumerationHelper
395    for NSDictionary<KeyType, ObjectType>
396{
397    // Fast enumeration for dictionaries returns the keys.
398    type Item = KeyType;
399
400    #[inline]
401    fn maybe_len(&self) -> Option<usize> {
402        Some(self.len())
403    }
404}
405
406#[cfg(feature = "NSEnumerator")]
407unsafe impl<KeyType: Message, ObjectType: Message> iter::FastEnumerationHelper
408    for NSMutableDictionary<KeyType, ObjectType>
409{
410    // The same goes for mutable dictionaries.
411    type Item = KeyType;
412
413    #[inline]
414    fn maybe_len(&self) -> Option<usize> {
415        Some(self.len())
416    }
417}
418
419/// An iterator over the keys of a dictionary.
420#[derive(Debug)]
421#[cfg(feature = "NSEnumerator")]
422pub struct Keys<'a, KeyType: Message, ObjectType: Message>(
423    iter::Iter<'a, NSDictionary<KeyType, ObjectType>>,
424);
425
426#[cfg(feature = "NSEnumerator")]
427__impl_iter! {
428    impl<'a, KeyType: Message, ObjectType: Message> Iterator<Item = Retained<KeyType>> for Keys<'a, KeyType, ObjectType> { ... }
429}
430
431/// An iterator over unretained keys of a dictionary.
432///
433/// # Safety
434///
435/// The dictionary must not be mutated while this is alive.
436#[derive(Debug)]
437#[cfg(feature = "NSEnumerator")]
438pub struct KeysUnchecked<'a, KeyType: Message, ObjectType: Message>(
439    iter::IterUnchecked<'a, NSDictionary<KeyType, ObjectType>>,
440);
441
442#[cfg(feature = "NSEnumerator")]
443__impl_iter! {
444    impl<'a, KeyType: Message, ObjectType: Message> Iterator<Item = &'a KeyType> for KeysUnchecked<'a, KeyType, ObjectType> { ... }
445}
446
447/// An iterator over the objects / values in a dictionary.
448#[derive(Debug)]
449#[cfg(feature = "NSEnumerator")]
450pub struct Objects<'a, KeyType: Message, ObjectType: Message>(
451    iter::IterWithBackingEnum<
452        'a,
453        NSDictionary<KeyType, ObjectType>,
454        crate::NSEnumerator<ObjectType>,
455    >,
456);
457
458#[cfg(feature = "NSEnumerator")]
459__impl_iter! {
460    impl<'a, KeyType: Message, ObjectType: Message> Iterator<Item = Retained<ObjectType>> for Objects<'a, KeyType, ObjectType> { ... }
461}
462
463/// An iterator over unretained objects / values of a dictionary.
464///
465/// # Safety
466///
467/// The dictionary must not be mutated while this is alive.
468#[derive(Debug)]
469#[cfg(feature = "NSEnumerator")]
470pub struct ObjectsUnchecked<'a, KeyType: Message, ObjectType: Message + 'a>(
471    iter::IterUncheckedWithBackingEnum<
472        'a,
473        NSDictionary<KeyType, ObjectType>,
474        crate::NSEnumerator<ObjectType>,
475    >,
476);
477
478#[cfg(feature = "NSEnumerator")]
479__impl_iter! {
480    impl<'a, KeyType: Message, ObjectType: Message> Iterator<Item = &'a ObjectType> for ObjectsUnchecked<'a, KeyType, ObjectType> { ... }
481}
482
483impl<KeyType: fmt::Debug + Message, ObjectType: fmt::Debug + Message> fmt::Debug
484    for NSDictionary<KeyType, ObjectType>
485{
486    #[inline]
487    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
488        // SAFETY: Unsound, use `to_vecs` instead when that doesn't have extra bounds
489        let (keys, objects) = unsafe { self.to_vecs_unchecked() };
490        let iter = keys.into_iter().zip(objects);
491        f.debug_map().entries(iter).finish()
492    }
493}
494
495impl<KeyType: fmt::Debug + Message, ObjectType: fmt::Debug + Message> fmt::Debug
496    for NSMutableDictionary<KeyType, ObjectType>
497{
498    #[inline]
499    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
500        fmt::Debug::fmt(&**self, f)
501    }
502}