objc2_foundation/
copying.rs

1use objc2::extern_protocol;
2use objc2::rc::Retained;
3use objc2::runtime::NSZone;
4use objc2::runtime::ProtocolObject;
5use objc2::Message;
6
7/// A helper type for implementing [`NSCopying`].
8///
9/// `NSCopying` and `NSMutableCopying` do not in their signatures describe the
10/// result type from the copying operation. This is problematic, as it means
11/// that using them ends up falling back to [`AnyObject`], which makes copying
12/// much less useful and ergonomic.
13///
14/// To properly describe this, we need an associated type which describes the
15/// actual result type from a copy. The associated type can't be present
16/// directly on the protocol traits themselves, however, since we want to use
17/// them as e.g. `ProtocolObject<dyn NSCopying>`, so we introduce this helper
18/// trait instead. See [`MutableCopyingHelper`] for the mutable variant.
19///
20/// We might be able to get rid of this hack once [associated type defaults]
21/// are stabilized.
22///
23/// [`AnyObject`]: objc2::runtime::AnyObject
24/// [associated type defaults]: https://github.com/rust-lang/rust/issues/29661
25///
26///
27/// # Safety
28///
29/// The [`Result`] type must be correct.
30///
31/// [`Result`]: Self::Result
32pub unsafe trait CopyingHelper: Message {
33    /// The immutable counterpart of the type, or `Self` if the type has no
34    /// immutable counterpart.
35    ///
36    /// The implementation for `NSString` has itself (`NSString`) here, while
37    /// `NSMutableString` instead has `NSString`.
38    type Result: Message;
39}
40
41/// A helper type for implementing [`NSMutableCopying`].
42///
43/// See [`CopyingHelper`] for the immutable variant, and more details in
44/// general. These traits are split to allow implementing
45/// `MutableCopyingHelper` only when the mutable class is available.
46///
47///
48/// # Safety
49///
50/// The [`Result`] type must be correct.
51///
52/// [`Result`]: Self::Result
53pub unsafe trait MutableCopyingHelper: Message {
54    /// The mutable counterpart of the type, or `Self` if the type has no
55    /// mutable counterpart.
56    ///
57    /// The implementation for `NSString` has `NSMutableString` here, while
58    /// `NSMutableString` has itself (`NSMutableString`).
59    type Result: Message;
60}
61
62// SAFETY: Superclasses are not in general required to implement the same
63// traits as their subclasses, but we're not dealing with normal classes and
64// arbitrary protocols, we're dealing with with immutable/mutable class
65// counterparts, and the `NSCopying`/`NSMutableCopying` protocols, which
66// _will_ be implemented on superclasses.
67unsafe impl<P: ?Sized> CopyingHelper for ProtocolObject<P> {
68    type Result = Self;
69}
70
71// SAFETY: Subclasses are required to always implement the same traits as
72// their superclasses, so a mutable subclass is required to implement the same
73// traits too.
74unsafe impl<P: ?Sized> MutableCopyingHelper for ProtocolObject<P> {
75    type Result = Self;
76}
77
78extern_protocol!(
79    /// A protocol to provide functional copies of objects.
80    ///
81    /// This is similar to Rust's [`Clone`] trait, along with sharing a few
82    /// similarities to the [`std::borrow::ToOwned`] trait with regards to the
83    /// output type.
84    ///
85    /// To allow using this in a meaningful way in Rust, we have to "enrich"
86    /// the implementation by also specifying the resulting type, see
87    /// [`CopyingHelper`] for details.
88    ///
89    /// See also [Apple's documentation][apple-doc].
90    ///
91    /// [apple-doc]: https://developer.apple.com/documentation/foundation/nscopying
92    ///
93    ///
94    /// # Examples
95    ///
96    /// Implement `NSCopying` for an externally defined class.
97    ///
98    /// ```
99    /// use objc2::extern_class;
100    /// use objc2_foundation::{CopyingHelper, NSCopying, NSObject};
101    ///
102    /// extern_class!(
103    ///     #[unsafe(super(NSObject))]
104    ///     # #[name = "NSData"]
105    ///     struct ExampleClass;
106    /// );
107    ///
108    /// unsafe impl NSCopying for ExampleClass {}
109    ///
110    /// // Copying ExampleClass returns another ExampleClass.
111    /// unsafe impl CopyingHelper for ExampleClass {
112    ///     type Result = Self;
113    /// }
114    /// ```
115    ///
116    /// Implement `NSCopying` for a custom class.
117    ///
118    /// ```
119    /// use objc2::{define_class, msg_send, AnyThread, DefinedClass};
120    /// use objc2::rc::Retained;
121    /// use objc2::runtime::NSZone;
122    /// use objc2_foundation::{CopyingHelper, NSCopying, NSObject};
123    ///
124    /// define_class!(
125    ///     #[unsafe(super(NSObject))]
126    ///     struct CustomClass;
127    ///
128    ///     unsafe impl NSCopying for CustomClass {
129    ///         #[unsafe(method_id(copyWithZone:))]
130    ///         fn copyWithZone(&self, _zone: *const NSZone) -> Retained<Self> {
131    ///             // Create new class, and transfer ivars
132    ///             let new = Self::alloc().set_ivars(self.ivars().clone());
133    ///             unsafe { msg_send![super(new), init] }
134    ///         }
135    ///     }
136    /// );
137    ///
138    /// // Copying CustomClass returns another CustomClass.
139    /// unsafe impl CopyingHelper for CustomClass {
140    ///     type Result = Self;
141    /// }
142    /// ```
143    #[allow(clippy::missing_safety_doc)]
144    pub unsafe trait NSCopying {
145        /// Returns a new instance that's a copy of the receiver.
146        ///
147        /// The output type is the immutable counterpart of the object, which
148        /// is usually `Self`, but e.g. `NSMutableString` returns `NSString`.
149        #[unsafe(method(copy))]
150        #[unsafe(method_family = copy)]
151        #[optional]
152        fn copy(&self) -> Retained<Self::Result>
153        where
154            Self: CopyingHelper;
155
156        /// Returns a new instance that's a copy of the receiver.
157        ///
158        /// This is only used when implementing `NSCopying`, call
159        /// [`copy`][NSCopying::copy] instead.
160        ///
161        ///
162        /// # Safety
163        ///
164        /// The zone pointer must be valid or NULL.
165        #[unsafe(method(copyWithZone:))]
166        #[unsafe(method_family = copy)]
167        unsafe fn copyWithZone(&self, zone: *mut NSZone) -> Retained<Self::Result>
168        where
169            Self: CopyingHelper;
170    }
171);
172
173extern_protocol!(
174    /// A protocol to provide mutable copies of objects.
175    ///
176    /// Only classes that have an “immutable vs. mutable” distinction should
177    /// adopt this protocol. Use the [`MutableCopyingHelper`] trait to specify
178    /// the return type after copying.
179    ///
180    /// See [Apple's documentation][apple-doc] for details.
181    ///
182    /// [apple-doc]: https://developer.apple.com/documentation/foundation/nsmutablecopying
183    ///
184    ///
185    /// # Example
186    ///
187    /// Implement [`NSCopying`] and [`NSMutableCopying`] for a class pair like
188    /// `NSString` and `NSMutableString`.
189    ///
190    /// ```ignore
191    /// // Immutable copies return NSString
192    ///
193    /// unsafe impl NSCopying for NSString {}
194    /// unsafe impl CopyingHelper for NSString {
195    ///     type Result = NSString;
196    /// }
197    /// unsafe impl NSCopying for NSMutableString {}
198    /// unsafe impl CopyingHelper for NSMutableString {
199    ///     type Result = NSString;
200    /// }
201    ///
202    /// // Mutable copies return NSMutableString
203    ///
204    /// unsafe impl NSMutableCopying for NSString {}
205    /// unsafe impl MutableCopyingHelper for NSString {
206    ///     type Result = NSMutableString;
207    /// }
208    /// unsafe impl NSMutableCopying for NSMutableString {}
209    /// unsafe impl MutableCopyingHelper for NSMutableString {
210    ///     type Result = NSMutableString;
211    /// }
212    /// ```
213    #[allow(clippy::missing_safety_doc)]
214    pub unsafe trait NSMutableCopying {
215        /// Returns a new instance that's a mutable copy of the receiver.
216        ///
217        /// The output type is the mutable counterpart of the object. E.g. both
218        /// `NSString` and `NSMutableString` return `NSMutableString`.
219        #[unsafe(method(mutableCopy))]
220        #[unsafe(method_family = mutableCopy)]
221        #[optional]
222        fn mutableCopy(&self) -> Retained<Self::Result>
223        where
224            Self: MutableCopyingHelper;
225
226        /// Returns a new instance that's a mutable copy of the receiver.
227        ///
228        /// This is only used when implementing `NSMutableCopying`, call
229        /// [`mutableCopy`][NSMutableCopying::mutableCopy] instead.
230        ///
231        ///
232        /// # Safety
233        ///
234        /// The zone pointer must be valid or NULL.
235        #[unsafe(method(mutableCopyWithZone:))]
236        #[unsafe(method_family = mutableCopy)]
237        unsafe fn mutableCopyWithZone(&self, zone: *mut NSZone) -> Retained<Self::Result>
238        where
239            Self: MutableCopyingHelper;
240    }
241);