Skip to content

Commit f52bc56

Browse files
committed
Create Owned and Retained using references instead of NonNull pointers
Makes the API easier to use, and means there are less safety requirements that we have to document.
1 parent 684159d commit f52bc56

File tree

2 files changed

+72
-37
lines changed

2 files changed

+72
-37
lines changed

src/rc/owned.rs

+36-19
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,18 @@
1-
use core::marker::PhantomData;
2-
use core::ptr::{NonNull, drop_in_place};
3-
use core::mem;
4-
use core::ops::{DerefMut, Deref};
1+
use core::borrow;
52
use core::fmt;
63
use core::hash;
7-
use core::borrow;
4+
use core::marker::PhantomData;
5+
use core::mem;
6+
use core::ops::{Deref, DerefMut};
7+
use core::ptr::{drop_in_place, NonNull};
88

99
use super::Retained;
1010
use crate::runtime::{self, Object};
1111

1212
/// A smart pointer that strongly references and owns an Objective-C object.
1313
///
14-
/// The fact that we own the pointer means that we're safe to mutate it, hence
15-
/// why this implements [`DerefMut`].
14+
/// The fact that we own the pointer means that it's safe to mutate it. As
15+
/// such, this implements [`DerefMut`].
1616
///
1717
/// This is guaranteed to have the same size as the underlying pointer.
1818
///
@@ -32,27 +32,44 @@ unsafe impl<T: Send> Send for Owned<T> {}
3232
// SAFETY: TODO
3333
unsafe impl<T: Sync> Sync for Owned<T> {}
3434

35+
// TODO: Unsure how the API should look...
3536
impl<T> Owned<T> {
37+
/// TODO
38+
///
39+
/// # Safety
40+
///
41+
/// The caller must ensure the given object reference has exactly 1 retain
42+
/// count (that is, a retain count that has been handed off from somewhere
43+
/// else, usually Objective-C methods like `init`, `alloc`, `new`, or
44+
/// `copy`).
45+
///
46+
/// Additionally, there must be no other pointers to the same object.
47+
///
48+
/// # Example
49+
///
50+
/// ```rust
51+
/// let obj: &mut Object = unsafe { msg_send![cls, alloc] };
52+
/// let obj: Owned<Object> = unsafe { Owned::new(msg_send![obj, init]) };
53+
/// // Or in this case simply just:
54+
/// let obj: Owned<Object> = unsafe { Owned::new(msg_send![cls, new]) };
55+
/// ```
56+
///
57+
/// TODO: Something about there not being other references.
3658
#[inline]
37-
pub unsafe fn new(ptr: NonNull<T>) -> Self {
59+
pub unsafe fn new(obj: &mut T) -> Self {
3860
Self {
39-
ptr,
61+
ptr: obj.into(),
4062
phantom: PhantomData,
4163
}
4264
}
4365

44-
// TODO: Unsure how the API should look...
45-
#[inline]
46-
pub unsafe fn retain(ptr: NonNull<T>) -> Self {
47-
Self::from_retained(Retained::retain(ptr))
48-
}
49-
50-
/// TODO
66+
/// Construct an `Owned` pointer
5167
///
5268
/// # Safety
5369
///
54-
/// The given [`Retained`] must be the only reference to the object
55-
/// anywhere in the program - even in other Objective-C code.
70+
/// The caller must ensure that there are no other pointers to the same
71+
/// object (which also means that the given [`Retained`] should have a
72+
/// retain count of exactly 1).
5673
#[inline]
5774
pub unsafe fn from_retained(obj: Retained<T>) -> Self {
5875
let ptr = mem::ManuallyDrop::new(obj).ptr;
@@ -77,7 +94,7 @@ impl<T> Drop for Owned<T> {
7794
unsafe {
7895
drop_in_place(ptr.as_ptr());
7996
// Construct a new `Retained`, which will be dropped immediately
80-
Retained::new(ptr);
97+
Retained::new(ptr.as_ref());
8198
};
8299
}
83100
}

src/rc/retained.rs

+36-18
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ use core::mem;
66
use core::ops::Deref;
77
use core::ptr::NonNull;
88

9-
use crate::runtime::{self, Object};
109
use super::Owned;
10+
use crate::runtime::{self, Object};
1111

1212
/// A smart pointer that strongly references an object, ensuring it won't be
1313
/// deallocated.
@@ -64,29 +64,33 @@ unsafe impl<T: Sync + Send> Send for Retained<T> {}
6464
// SAFETY: TODO
6565
unsafe impl<T: Sync + Send> Sync for Retained<T> {}
6666

67-
6867
impl<T> Retained<T> {
6968
/// Constructs a `Retained<T>` to an object that already has a +1 retain
7069
/// count. This will not retain the object.
7170
///
7271
/// When dropped, the object will be released.
7372
///
73+
/// See also [`Owned::new`] for the common case of creating objects.
74+
///
7475
/// # Safety
7576
///
76-
/// The caller must ensure the given object pointer is valid, and has +1
77-
/// retain count.
77+
/// The caller must ensure the given object reference has +1 retain count
78+
/// (that is, a retain count that has been handed off from somewhere else,
79+
/// usually Objective-C methods with the `ns_returns_retained` attribute).
80+
///
81+
/// Additionally, there must be no [`Owned`] pointers to the same object.
7882
///
7983
/// TODO: Something about there not being any mutable references.
8084
#[inline]
81-
pub unsafe fn new(ptr: NonNull<T>) -> Self {
85+
pub unsafe fn new(obj: &T) -> Self {
8286
Self {
83-
ptr,
87+
ptr: obj.into(),
8488
phantom: PhantomData,
8589
}
8690
}
8791

8892
#[inline]
89-
pub fn as_ptr(&self) -> *mut T {
93+
pub fn as_ptr(&self) -> *const T {
9094
self.ptr.as_ptr()
9195
}
9296

@@ -96,16 +100,28 @@ impl<T> Retained<T> {
96100
///
97101
/// # Safety
98102
///
99-
/// The caller must ensure the given object pointer is valid.
103+
/// The caller must ensure that there are no [`Owned`] pointers to the
104+
/// same object.
105+
//
106+
// So this would be illegal:
107+
// ```rust
108+
// let owned: Owned<T> = ...;
109+
// // Lifetime information is discarded
110+
// let retained = Retained::retain(&*owned);
111+
// // Which means we can still mutate `Owned`:
112+
// let x: &mut T = &mut *owned;
113+
// // While we have an immutable reference
114+
// let y: &T = &*retained;
115+
// ```
100116
#[doc(alias = "objc_retain")]
101-
#[inline]
102-
// TODO: Maybe just take a normal reference, and then this can be safe?
103-
pub unsafe fn retain(ptr: NonNull<T>) -> Self {
117+
// Inlined since it's `objc_retain` that does the work.
118+
#[cfg_attr(debug_assertions, inline)]
119+
pub unsafe fn retain(obj: &T) -> Self {
104120
// SAFETY: The caller upholds that the pointer is valid
105-
let rtn = runtime::objc_retain(ptr.as_ptr() as *mut Object);
106-
debug_assert_eq!(rtn, ptr.as_ptr() as *mut Object);
121+
let rtn = runtime::objc_retain(obj as *const T as *mut Object);
122+
debug_assert_eq!(rtn, obj as *const T as *mut Object);
107123
Self {
108-
ptr,
124+
ptr: obj.into(),
109125
phantom: PhantomData,
110126
}
111127
}
@@ -119,6 +135,8 @@ impl<T> Retained<T> {
119135
#[doc(alias = "objc_autorelease")]
120136
#[must_use = "If you don't intend to use the object any more, just drop it as usual"]
121137
#[inline]
138+
// TODO: Get a lifetime relating to the pool, so that we can return a
139+
// reference instead of a pointer.
122140
pub fn autorelease(self) -> NonNull<T> {
123141
let ptr = mem::ManuallyDrop::new(self).ptr;
124142
// SAFETY: The `ptr` is guaranteed to be valid and have at least one
@@ -159,7 +177,7 @@ impl<T> Clone for Retained<T> {
159177
#[inline]
160178
fn clone(&self) -> Self {
161179
// SAFETY: The `ptr` is guaranteed to be valid
162-
unsafe { Self::retain(self.ptr) }
180+
unsafe { Self::retain(&*self) }
163181
}
164182
}
165183

@@ -230,7 +248,7 @@ impl<T> Unpin for Retained<T> {}
230248
impl<T> From<Owned<T>> for Retained<T> {
231249
fn from(obj: Owned<T>) -> Self {
232250
// SAFETY: TODO
233-
unsafe { Self::new(obj.ptr) }
251+
unsafe { Self::new(&*obj) }
234252
}
235253
}
236254

@@ -259,8 +277,8 @@ mod tests {
259277
#[test]
260278
fn test_clone() {
261279
// TODO: Maybe make a way to return `Retained` directly?
262-
let obj: *mut Object = unsafe { msg_send![class!(NSObject), new] };
263-
let obj: Retained<Object> = unsafe { Retained::new(NonNull::new(obj).unwrap()) };
280+
let obj: &Object = unsafe { msg_send![class!(NSObject), new] };
281+
let obj: Retained<Object> = unsafe { Retained::new(obj) };
264282
assert!(obj.retain_count() == 1);
265283

266284
let cloned = obj.clone();

0 commit comments

Comments
 (0)