@@ -108,16 +108,21 @@ impl Drop for Pool {
108108/// use objc2::runtime::NSObject;
109109/// use objc2::msg_send;
110110///
111- /// fn needs_lifetime_from_pool<'p>(pool: AutoreleasePool<'p>) -> &'p NSObject {
111+ /// unsafe fn needs_lifetime_from_pool<'p>(pool: AutoreleasePool<'p>) -> &'p NSObject {
112112/// let obj = NSObject::new();
113113/// // Do action that returns an autoreleased object
114114/// let description: *mut NSObject = unsafe { msg_send![&obj, description] };
115- /// // Bound the lifetime of the reference to the pool
115+ /// // Bound the lifetime of the reference to that of the pool.
116+ /// //
117+ /// // Note that this only helps ensuring soundness, our function is still
118+ /// // unsafe because the pool cannot be guaranteed to be the innermost
119+ /// // pool.
116120/// unsafe { pool.ptr_as_ref(description) }
117121/// }
118122///
119123/// autoreleasepool(|pool| {
120- /// let obj = needs_lifetime_from_pool(pool);
124+ /// // SAFETY: The given pool is the innermost pool.
125+ /// let obj = unsafe { needs_lifetime_from_pool(pool) };
121126/// println!("{obj:?}");
122127/// });
123128/// ```
@@ -178,10 +183,8 @@ impl<'pool> AutoreleasePool<'pool> {
178183 }
179184 }
180185
181- /// This will be removed in a future version.
182186 #[ inline]
183- #[ doc( hidden) ]
184- pub fn __verify_is_inner ( self ) {
187+ pub ( crate ) fn __verify_is_inner ( self ) {
185188 #[ cfg( all( debug_assertions, not( feature = "unstable-autoreleasesafe" ) ) ) ]
186189 if let Some ( pool) = & self . inner {
187190 POOLS . with ( |c| {
@@ -200,11 +203,24 @@ impl<'pool> AutoreleasePool<'pool> {
200203 /// objects, since it binds the lifetime of the reference to the pool, and
201204 /// does some extra checks when debug assertions are enabled.
202205 ///
206+ /// Note that this is helpful, but not sufficient, for ensuring that the
207+ /// lifetime of the reference does not exceed the lifetime of the
208+ /// autorelease pool. When calling this, you must also ensure that the
209+ /// pool is actually the current innermost pool.
210+ ///
211+ ///
212+ /// # Panics
213+ ///
214+ /// If the pool is not the innermost pool, this function may panic when
215+ /// the `"std"` Cargo feature and debug assertions are enabled.
216+ ///
203217 ///
204218 /// # Safety
205219 ///
206220 /// This is equivalent to `&*ptr`, and shares the unsafety of that, except
207221 /// the lifetime is bound to the pool instead of being unbounded.
222+ ///
223+ /// The pool must be the innermost pool for the lifetime to be correct.
208224 #[ inline]
209225 pub unsafe fn ptr_as_ref < T : ?Sized > ( self , ptr : * const T ) -> & ' pool T {
210226 self . __verify_is_inner ( ) ;
@@ -304,51 +320,66 @@ impl !AutoreleaseSafe for AutoreleasePool<'_> {}
304320/// see [Apple's documentation][apple-autorelease] for general information on
305321/// when to use those.
306322///
307- /// The pool is passed as a parameter to the closure to give you a lifetime
323+ /// [ The pool] is passed as a parameter to the closure to give you a lifetime
308324/// parameter that autoreleased objects can refer to.
309325///
310326/// Note that this is mostly useful for preventing leaks (as any Objective-C
311327/// method may autorelease internally - see also [`autoreleasepool_leaking`]).
312328/// If implementing an interface to an object, you should try to return
313329/// retained pointers with [`msg_send_id!`] wherever you can instead, since
314- /// it is usually more efficient, and having to use this function can be quite
315- /// cumbersome for users.
330+ /// it is usually more efficient, safer, and having to use this function can
331+ /// be quite cumbersome for users.
316332///
317333/// [apple-autorelease]: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html
334+ /// [The pool]: AutoreleasePool
318335/// [`msg_send_id!`]: crate::msg_send_id
319336///
320337///
321338/// # Restrictions
322339///
323- /// The given parameter must not be used in an inner `autoreleasepool` - doing
324- /// so will panic with debug assertions enabled, and be a compile error in a
325- /// future release .
340+ /// The given parameter must not be used inside a nested `autoreleasepool`,
341+ /// since doing so will give the objects that it is used with an incorrect
342+ /// lifetime bound .
326343///
327- /// Note that this means that **this function is currently unsound**, since it
328- /// doesn't disallow wrong usage in all cases. Enabling the assertions in
329- /// release mode would be prohibitively expensive though, so this is the
330- /// least-bad solution.
344+ /// You can try to enable the `"unstable-autoreleasesafe"` Cargo feature using
345+ /// nightly Rust - if your use of this function compiles with that, it is more
346+ /// likely to be correct, though note that Rust does not have a way to express
347+ /// the lifetimes involved, so we cannot detect all invalid usage. See issue
348+ /// [#540] for details.
331349///
332- /// You can try to compile your crate with the `"unstable-autoreleasesafe"`
333- /// crate feature enabled on nightly Rust - if your crate compiles with that,
334- /// its autoreleasepool usage is guaranteed to be correct.
350+ /// [#540]: https://github.com/madsmtm/objc2/issues/540
335351///
336352///
337353/// # Examples
338354///
339- /// Basic usage:
355+ /// Use an external API, and ensure that the memory that it used is cleaned
356+ /// up afterwards.
357+ ///
358+ /// ```
359+ /// use objc2::rc::autoreleasepool;
360+ /// # fn example_function() {}
361+ /// # #[cfg(for_illustrative_purposes)]
362+ /// use example_crate::example_function;
363+ ///
364+ /// autoreleasepool(|_| {
365+ /// // Call `example_function` in the context of a pool
366+ /// example_function();
367+ /// }); // Memory released into the pool is cleaned up when the scope ends
368+ /// ```
369+ ///
370+ /// Autorelease an object into an autorelease pool:
340371///
341372/// ```
342373/// use objc2::rc::{autoreleasepool, Retained};
343374/// use objc2::runtime::NSObject;
344375///
345376/// autoreleasepool(|pool| {
346377/// // Create `obj` and autorelease it to the pool
347- /// let obj = Retained::autorelease(NSObject::new(), pool);
378+ /// // SAFETY: The pool is the current innermost pool
379+ /// let obj = unsafe { Retained::autorelease(NSObject::new(), pool) };
348380/// // We now have a reference that we can freely use
349381/// println!("{obj:?}");
350- /// // `obj` is deallocated when the pool ends
351- /// });
382+ /// }); // `obj` is deallocated when the pool ends
352383/// // And is no longer usable outside the closure
353384/// ```
354385///
@@ -359,7 +390,7 @@ impl !AutoreleaseSafe for AutoreleasePool<'_> {}
359390/// use objc2::rc::{autoreleasepool, Retained};
360391/// use objc2::runtime::NSObject;
361392///
362- /// let obj = autoreleasepool(|pool| {
393+ /// let obj = autoreleasepool(|pool| unsafe {
363394/// Retained::autorelease(NSObject::new(), pool)
364395/// });
365396/// ```
@@ -375,7 +406,8 @@ impl !AutoreleaseSafe for AutoreleasePool<'_> {}
375406///
376407/// autoreleasepool(|outer_pool| {
377408/// let obj = autoreleasepool(|inner_pool| {
378- /// Retained::autorelease(NSObject::new(), outer_pool)
409+ /// // SAFETY: NOT safe, the pool is _not_ the innermost pool!
410+ /// unsafe { Retained::autorelease(NSObject::new(), outer_pool) }
379411/// });
380412/// // `obj` could wrongly be used here because its lifetime was
381413/// // assigned to the outer pool, even though it was released by the
@@ -444,7 +476,8 @@ where
444476///
445477/// autoreleasepool(|outer_pool| {
446478/// let obj = autoreleasepool_leaking(|inner_pool| {
447- /// Retained::autorelease(NSObject::new(), outer_pool)
479+ /// // SAFETY: The given `outer_pool` is the actual innermost pool.
480+ /// unsafe { Retained::autorelease(NSObject::new(), outer_pool) }
448481/// });
449482/// // `obj` is still usable here, since the leaking pool doesn't actually
450483/// // do anything.
@@ -461,8 +494,8 @@ where
461494/// use objc2::rc::{autoreleasepool_leaking, Retained};
462495/// use objc2::runtime::NSObject;
463496///
464- /// let obj = autoreleasepool_leaking(|pool| {
465- /// Retained::autorelease(NSObject::new(), pool)
497+ /// let obj = autoreleasepool_leaking(|pool| unsafe {
498+ /// unsafe { Retained::autorelease(NSObject::new(), pool) }
466499/// });
467500/// ```
468501///
@@ -476,7 +509,8 @@ where
476509///
477510/// autoreleasepool_leaking(|outer_pool| {
478511/// let obj = autoreleasepool(|inner_pool| {
479- /// Retained::autorelease(NSObject::new(), outer_pool)
512+ /// // SAFETY: NOT safe, the pool is _not_ the innermost pool!
513+ /// unsafe { Retained::autorelease(NSObject::new(), outer_pool) }
480514/// });
481515/// });
482516/// #
@@ -564,7 +598,7 @@ mod tests {
564598 let mut expected = expected;
565599
566600 autoreleasepool ( |pool| {
567- let _autoreleased = Retained :: autorelease ( obj. 0 , pool) ;
601+ let _autoreleased = unsafe { Retained :: autorelease ( obj. 0 , pool) } ;
568602 expected. autorelease += 1 ;
569603 expected. assert_current ( ) ;
570604 panic ! ( "unwind" ) ;
0 commit comments