Skip to content

Commit b9c31ff

Browse files
committed
Document safety of array creation methods
1 parent 747baac commit b9c31ff

File tree

2 files changed

+50
-14
lines changed

2 files changed

+50
-14
lines changed

objc2/src/foundation/array.rs

+38-13
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ pub(crate) unsafe fn with_objects<T: Message + ?Sized, R: Message, O: Ownership>
8585
}
8686
}
8787

88+
/// Generic creation methods.
8889
impl<T: Message, O: Ownership> NSArray<T, O> {
8990
/// Get an empty array.
9091
pub fn new() -> Id<Self, Shared> {
@@ -96,8 +97,39 @@ impl<T: Message, O: Ownership> NSArray<T, O> {
9697
// hence the notion of ownership over the elements is void.
9798
unsafe { msg_send_id![Self::class(), new].expect("unexpected NULL NSArray") }
9899
}
100+
101+
pub fn from_vec(vec: Vec<Id<T, O>>) -> Id<Self, O> {
102+
// SAFETY:
103+
// `initWithObjects:` may choose to deduplicate arrays (I could
104+
// imagine it having a special case for arrays with one `NSNumber`
105+
// object), and returning mutable references to those would be
106+
// unsound!
107+
// However, when we know that we have ownership over the variables, we
108+
// also know that there cannot be another array in existence with the
109+
// same objects, so `Id<NSArray<T, Owned>, Owned>` is safe to return.
110+
//
111+
// In essence, we can choose between always returning `Id<T, Shared>`
112+
// or `Id<T, O>`, and the latter is probably the most useful, as we
113+
// would like to know when we're the only owner of the array, to
114+
// allow mutation of the array's items.
115+
unsafe { with_objects(Self::class(), vec.as_slice_ref()) }
116+
}
117+
}
118+
119+
/// Creation methods that produce shared arrays.
120+
impl<T: Message> NSArray<T, Shared> {
121+
pub fn from_slice(slice: &[Id<T, Shared>]) -> Id<Self, Shared> {
122+
// SAFETY: Taking `&T` would not be sound, since the `&T` could come
123+
// from an `Id<T, Owned>` that would now no longer be owned!
124+
//
125+
// (Note that NSArray internally retains all the objects it is given,
126+
// effectively making the safety requirements the same as
127+
// `Id::retain`).
128+
unsafe { with_objects(Self::class(), slice.as_slice_ref()) }
129+
}
99130
}
100131

132+
/// Generic accessor methods.
101133
impl<T: Message, O: Ownership> NSArray<T, O> {
102134
#[doc(alias = "count")]
103135
pub fn len(&self) -> usize {
@@ -137,13 +169,6 @@ impl<T: Message, O: Ownership> NSArray<T, O> {
137169
}
138170
}
139171

140-
// The `NSArray` itself (length and number of items) is always immutable,
141-
// but we would like to know when we're the only owner of the array, to
142-
// allow mutation of the array's items.
143-
pub fn from_vec(vec: Vec<Id<T, O>>) -> Id<Self, O> {
144-
unsafe { with_objects(Self::class(), vec.as_slice_ref()) }
145-
}
146-
147172
pub fn objects_in_range(&self, range: Range<usize>) -> Vec<&T> {
148173
let range = NSRange::from(range);
149174
let mut vec = Vec::with_capacity(range.length);
@@ -168,11 +193,8 @@ impl<T: Message, O: Ownership> NSArray<T, O> {
168193
}
169194
}
170195

196+
/// Accessor methods that work on shared arrays.
171197
impl<T: Message> NSArray<T, Shared> {
172-
pub fn from_slice(slice: &[Id<T, Shared>]) -> Id<Self, Shared> {
173-
unsafe { with_objects(Self::class(), slice.as_slice_ref()) }
174-
}
175-
176198
#[doc(alias = "objectAtIndex:")]
177199
pub fn get_retained(&self, index: usize) -> Id<T, Shared> {
178200
let obj = self.get(index).unwrap();
@@ -188,6 +210,7 @@ impl<T: Message> NSArray<T, Shared> {
188210
}
189211
}
190212

213+
/// Accessor methods that work on owned arrays.
191214
impl<T: Message> NSArray<T, Owned> {
192215
#[doc(alias = "objectAtIndex:")]
193216
pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
@@ -211,13 +234,15 @@ impl<T: Message> NSArray<T, Owned> {
211234
}
212235
}
213236

214-
// Copying only possible when ItemOwnership = Shared
215-
237+
/// This is implemented as a shallow copy.
238+
///
239+
/// As such, it is only possible when the array's contents are `Shared`.
216240
unsafe impl<T: Message> NSCopying for NSArray<T, Shared> {
217241
type Ownership = Shared;
218242
type Output = NSArray<T, Shared>;
219243
}
220244

245+
/// This is implemented as a shallow copy.
221246
unsafe impl<T: Message> NSMutableCopying for NSArray<T, Shared> {
222247
type Output = NSMutableArray<T, Shared>;
223248
}

objc2/src/foundation/mutable_array.rs

+12-1
Original file line numberDiff line numberDiff line change
@@ -35,22 +35,31 @@ unsafe impl<T: Message + Sync + Send> Send for NSMutableArray<T, Shared> {}
3535
unsafe impl<T: Message + Sync> Sync for NSMutableArray<T, Owned> {}
3636
unsafe impl<T: Message + Send> Send for NSMutableArray<T, Owned> {}
3737

38+
/// Generic creation methods.
3839
impl<T: Message, O: Ownership> NSMutableArray<T, O> {
3940
pub fn new() -> Id<Self, Owned> {
40-
unsafe { msg_send_id![Self::class(), new].unwrap() }
41+
// SAFETY: Same as `NSArray::new`, except mutable arrays are always
42+
// unique.
43+
unsafe { msg_send_id![Self::class(), new].expect("unexpected NULL NSMutableArray") }
4144
}
4245

4346
pub fn from_vec(vec: Vec<Id<T, O>>) -> Id<Self, Owned> {
47+
// SAFETY: Same as `NSArray::from_vec`, except mutable arrays are
48+
// always unique.
4449
unsafe { with_objects(Self::class(), vec.as_slice_ref()) }
4550
}
4651
}
4752

53+
/// Creation methods that produce shared arrays.
4854
impl<T: Message> NSMutableArray<T, Shared> {
4955
pub fn from_slice(slice: &[Id<T, Shared>]) -> Id<Self, Owned> {
56+
// SAFETY: Same as `NSArray::from_slice`, except mutable arrays are
57+
// always unique.
5058
unsafe { with_objects(Self::class(), slice.as_slice_ref()) }
5159
}
5260
}
5361

62+
/// Generic accessor methods.
5463
impl<T: Message, O: Ownership> NSMutableArray<T, O> {
5564
#[doc(alias = "addObject:")]
5665
pub fn push(&mut self, obj: Id<T, O>) {
@@ -158,11 +167,13 @@ impl<T: Message, O: Ownership> NSMutableArray<T, O> {
158167

159168
// Copying only possible when ItemOwnership = Shared
160169

170+
/// This is implemented as a shallow copy.
161171
unsafe impl<T: Message> NSCopying for NSMutableArray<T, Shared> {
162172
type Ownership = Shared;
163173
type Output = NSArray<T, Shared>;
164174
}
165175

176+
/// This is implemented as a shallow copy.
166177
unsafe impl<T: Message> NSMutableCopying for NSMutableArray<T, Shared> {
167178
type Output = NSMutableArray<T, Shared>;
168179
}

0 commit comments

Comments
 (0)