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, AllocAnyThread, 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    ///     #[name = "CustomClass"]
127    ///     struct CustomClass;
128    ///
129    ///     unsafe impl NSCopying for CustomClass {
130    ///         #[unsafe(method_id(copyWithZone:))]
131    ///         fn copyWithZone(&self, _zone: *const NSZone) -> Retained<Self> {
132    ///             // Create new class, and transfer ivars
133    ///             let new = Self::alloc().set_ivars(self.ivars().clone());
134    ///             unsafe { msg_send![super(new), init] }
135    ///         }
136    ///     }
137    /// );
138    ///
139    /// // Copying CustomClass returns another CustomClass.
140    /// unsafe impl CopyingHelper for CustomClass {
141    ///     type Result = Self;
142    /// }
143    /// ```
144    #[allow(clippy::missing_safety_doc)]
145    pub unsafe trait NSCopying {
146        /// Returns a new instance that's a copy of the receiver.
147        ///
148        /// The output type is the immutable counterpart of the object, which
149        /// is usually `Self`, but e.g. `NSMutableString` returns `NSString`.
150        #[unsafe(method(copy))]
151        #[unsafe(method_family = copy)]
152        #[optional]
153        fn copy(&self) -> Retained<Self::Result>
154        where
155            Self: CopyingHelper;
156
157        /// Returns a new instance that's a copy of the receiver.
158        ///
159        /// This is only used when implementing `NSCopying`, call
160        /// [`copy`][NSCopying::copy] instead.
161        ///
162        ///
163        /// # Safety
164        ///
165        /// The zone pointer must be valid or NULL.
166        #[unsafe(method(copyWithZone:))]
167        #[unsafe(method_family = copy)]
168        unsafe fn copyWithZone(&self, zone: *mut NSZone) -> Retained<Self::Result>
169        where
170            Self: CopyingHelper;
171    }
172);
173
174extern_protocol!(
175    /// A protocol to provide mutable copies of objects.
176    ///
177    /// Only classes that have an “immutable vs. mutable” distinction should
178    /// adopt this protocol. Use the [`MutableCopyingHelper`] trait to specify
179    /// the return type after copying.
180    ///
181    /// See [Apple's documentation][apple-doc] for details.
182    ///
183    /// [apple-doc]: https://developer.apple.com/documentation/foundation/nsmutablecopying
184    ///
185    ///
186    /// # Example
187    ///
188    /// Implement [`NSCopying`] and [`NSMutableCopying`] for a class pair like
189    /// `NSString` and `NSMutableString`.
190    ///
191    /// ```ignore
192    /// // Immutable copies return NSString
193    ///
194    /// unsafe impl NSCopying for NSString {}
195    /// unsafe impl CopyingHelper for NSString {
196    ///     type Result = NSString;
197    /// }
198    /// unsafe impl NSCopying for NSMutableString {}
199    /// unsafe impl CopyingHelper for NSMutableString {
200    ///     type Result = NSString;
201    /// }
202    ///
203    /// // Mutable copies return NSMutableString
204    ///
205    /// unsafe impl NSMutableCopying for NSString {}
206    /// unsafe impl MutableCopyingHelper for NSString {
207    ///     type Result = NSMutableString;
208    /// }
209    /// unsafe impl NSMutableCopying for NSMutableString {}
210    /// unsafe impl MutableCopyingHelper for NSMutableString {
211    ///     type Result = NSMutableString;
212    /// }
213    /// ```
214    #[allow(clippy::missing_safety_doc)]
215    pub unsafe trait NSMutableCopying {
216        /// Returns a new instance that's a mutable copy of the receiver.
217        ///
218        /// The output type is the mutable counterpart of the object. E.g. both
219        /// `NSString` and `NSMutableString` return `NSMutableString`.
220        #[unsafe(method(mutableCopy))]
221        #[unsafe(method_family = mutableCopy)]
222        #[optional]
223        fn mutableCopy(&self) -> Retained<Self::Result>
224        where
225            Self: MutableCopyingHelper;
226
227        /// Returns a new instance that's a mutable copy of the receiver.
228        ///
229        /// This is only used when implementing `NSMutableCopying`, call
230        /// [`mutableCopy`][NSMutableCopying::mutableCopy] instead.
231        ///
232        ///
233        /// # Safety
234        ///
235        /// The zone pointer must be valid or NULL.
236        #[unsafe(method(mutableCopyWithZone:))]
237        #[unsafe(method_family = mutableCopy)]
238        unsafe fn mutableCopyWithZone(&self, zone: *mut NSZone) -> Retained<Self::Result>
239        where
240            Self: MutableCopyingHelper;
241    }
242);