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);