diff --git a/objc2-foundation/src/object.rs b/objc2-foundation/src/object.rs index 290c4d761..d797a0b15 100644 --- a/objc2-foundation/src/object.rs +++ b/objc2-foundation/src/object.rs @@ -17,7 +17,7 @@ pub unsafe trait INSObject: Message { fn is_equal(&self, other: &T) -> bool { let result: Bool = unsafe { msg_send![self, isEqual: other] }; - result.is_true() + result.as_bool() } fn description(&self) -> Id { @@ -30,7 +30,7 @@ pub unsafe trait INSObject: Message { fn is_kind_of(&self, cls: &Class) -> bool { let result: Bool = unsafe { msg_send![self, isKindOfClass: cls] }; - result.is_true() + result.as_bool() } } diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index 6350d690b..a3390f514 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -10,6 +10,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added deprecated `Object::get_ivar` and `Object::get_mut_ivar` to make upgrading easier. * Allow using `From`/`TryFrom` to convert between `rc::Id` and `rc::WeakId`. +* Added `Bool::as_bool` (more descriptive name than `Bool::is_true`). +* Added convenience methods `Id::new_null`, `Id::as_ptr` and + `Id::retain_null`. ## 0.3.0-alpha.6 - 2022-01-03 diff --git a/objc2/src/bool.rs b/objc2/src/bool.rs index d4fbc2d47..e6b94a740 100644 --- a/objc2/src/bool.rs +++ b/objc2/src/bool.rs @@ -4,8 +4,8 @@ use core::fmt; /// The Objective-C `BOOL` type. /// /// This is a thin wrapper-type over [`objc_sys::BOOL`]. It is intended that -/// you convert this into a Rust [`bool`] with the [`Bool::is_false`] or -/// [`Bool::is_true`] methods as soon as possible. +/// you convert this into a Rust [`bool`] with the [`Bool::as_bool`] method as +/// soon as possible. /// /// This is FFI-safe and can be used in directly with /// [`msg_send!`][`crate::msg_send`]. @@ -20,12 +20,12 @@ use core::fmt; /// use objc2::runtime::{Object, Bool}; /// let ns_value: *mut Object = unsafe { msg_send![class!(NSValue), initWithBool: Bool::YES] }; /// let rtn: Bool = unsafe { msg_send![ns_value, boolValue] }; -/// assert!(rtn.is_true()); +/// assert!(rtn.as_bool()); /// ``` #[repr(transparent)] // We don't implement comparison traits because they could be implemented with // two slightly different semantics: -// - `self.is_true().cmp(other.is_true())` +// - `self.as_bool().cmp(other.as_bool())` // - `self.value.cmp(other.value)` // And it is not immediately clear for users which one was chosen. #[derive(Copy, Clone, Default)] @@ -63,6 +63,8 @@ impl Bool { } /// Returns `true` if `self` is [`NO`][`Self::NO`]. + /// + /// You should prefer using [`as_bool`][`Self::as_bool`]. #[inline] pub const fn is_false(self) -> bool { // Always compare with 0 @@ -71,12 +73,20 @@ impl Bool { } /// Returns `true` if `self` is the opposite of [`NO`][`Self::NO`]. + /// + /// You should prefer using [`as_bool`][`Self::as_bool`]. #[inline] pub const fn is_true(self) -> bool { // Always compare with 0 // This is what happens when using `if` in C. self.value as u8 != 0 } + + /// Converts this into the [`bool`] equivalent. + #[inline] + pub const fn as_bool(self) -> bool { + self.is_true() + } } impl From for Bool { @@ -89,13 +99,13 @@ impl From for Bool { impl From for bool { #[inline] fn from(b: Bool) -> bool { - b.is_true() + b.as_bool() } } impl fmt::Debug for Bool { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str(if self.is_true() { "YES" } else { "NO" }) + f.write_str(if self.as_bool() { "YES" } else { "NO" }) } } @@ -125,12 +135,14 @@ mod tests { #[test] fn test_basic() { let b = Bool::new(true); + assert!(b.as_bool()); assert!(b.is_true()); assert!(!b.is_false()); assert!(bool::from(b)); assert_eq!(b.as_raw() as usize, 1); let b = Bool::new(false); + assert!(!b.as_bool()); assert!(!b.is_true()); assert!(b.is_false()); assert!(!bool::from(b)); @@ -140,10 +152,12 @@ mod tests { #[test] fn test_associated_constants() { let b = Bool::YES; + assert!(b.as_bool()); assert!(b.is_true()); assert_eq!(b.as_raw() as usize, 1); let b = Bool::NO; + assert!(!b.as_bool()); assert!(b.is_false()); assert_eq!(b.as_raw() as usize, 0); } @@ -151,8 +165,10 @@ mod tests { #[test] fn test_impls() { let b: Bool = Default::default(); + assert!(!b.as_bool()); assert!(b.is_false()); + assert!(Bool::from(true).as_bool()); assert!(Bool::from(true).is_true()); assert!(Bool::from(false).is_false()); diff --git a/objc2/src/declare.rs b/objc2/src/declare.rs index c5128d7e5..de3bf3c72 100644 --- a/objc2/src/declare.rs +++ b/objc2/src/declare.rs @@ -215,7 +215,7 @@ impl ClassDecl { types.as_ptr(), ) }); - assert!(success.is_true(), "Failed to add method {:?}", sel); + assert!(success.as_bool(), "Failed to add method {:?}", sel); } /// Adds a class method with the given name and implementation. @@ -253,7 +253,7 @@ impl ClassDecl { types.as_ptr(), ) }); - assert!(success.is_true(), "Failed to add class method {:?}", sel); + assert!(success.as_bool(), "Failed to add class method {:?}", sel); } /// Adds an ivar with type `T` and the provided name. @@ -275,7 +275,7 @@ impl ClassDecl { encoding.as_ptr(), ) }); - assert!(success.is_true(), "Failed to add ivar {}", name); + assert!(success.as_bool(), "Failed to add ivar {}", name); } /// Adds the given protocol to self. @@ -285,7 +285,7 @@ impl ClassDecl { /// If the protocol wasn't successfully added. pub fn add_protocol(&mut self, proto: &Protocol) { let success = unsafe { ffi::class_addProtocol(self.cls as _, proto.as_ptr()) }; - let success = Bool::from_raw(success).is_true(); + let success = Bool::from_raw(success).as_bool(); assert!(success, "Failed to add protocol {:?}", proto); } diff --git a/objc2/src/exception.rs b/objc2/src/exception.rs index 29dd7f95c..31f950af0 100644 --- a/objc2/src/exception.rs +++ b/objc2/src/exception.rs @@ -68,7 +68,7 @@ extern "C" { #[inline] pub unsafe fn throw(exception: Option<&Id>) -> ! { let exception = match exception { - Some(id) => &**id as *const Object as *mut ffi::objc_object, + Some(id) => id.as_ptr() as *mut ffi::objc_object, None => ptr::null_mut(), }; unsafe { ffi::objc_exception_throw(exception) } diff --git a/objc2/src/lib.rs b/objc2/src/lib.rs index f2be6df64..eda98a0ba 100644 --- a/objc2/src/lib.rs +++ b/objc2/src/lib.rs @@ -44,7 +44,7 @@ //! // Usage //! let hash: NSUInteger = unsafe { msg_send![obj, hash] }; //! let is_kind: Bool = unsafe { msg_send![obj, isKindOfClass: cls] }; -//! assert!(is_kind.is_true()); +//! assert!(is_kind.as_bool()); //! ``` //! //! Note that this very simple example contains **a lot** of `unsafe` (which diff --git a/objc2/src/message/mod.rs b/objc2/src/message/mod.rs index 3b2e530b0..14a353189 100644 --- a/objc2/src/message/mod.rs +++ b/objc2/src/message/mod.rs @@ -252,8 +252,7 @@ unsafe impl MessageReceiver for NonNull { unsafe impl MessageReceiver for Id { #[inline] fn as_raw_receiver(&self) -> *mut Object { - // TODO: Maybe don't dereference here, just to be safe? - (&**self).as_raw_receiver() + self.as_ptr() as *mut Object } } @@ -449,7 +448,7 @@ mod tests { }; assert_eq!(result, 4); - let obj: *const ManuallyDrop = (&**obj as *const Object).cast(); + let obj: *const ManuallyDrop = obj.as_ptr().cast(); let result: u32 = unsafe { msg_send![obj, foo] }; assert_eq!(result, 4); } diff --git a/objc2/src/rc/id.rs b/objc2/src/rc/id.rs index 44ecc956f..a16a8d79b 100644 --- a/objc2/src/rc/id.rs +++ b/objc2/src/rc/id.rs @@ -173,6 +173,33 @@ impl Id { own: PhantomData, } } + + /// Constructs an [`Id`] from a pointer that may be null. + /// + /// This is just a convenience wrapper over [`Id::new`] so that you don't + /// need to construct a [`NonNull`] when you know the pointer may be null. + /// + /// # Safety + /// + /// Same as [`Id::new`]. + #[inline] + pub unsafe fn new_null(ptr: *mut T) -> Option> { + // SAFETY: Upheld by the caller + NonNull::new(ptr).map(|ptr| unsafe { Id::new(ptr) }) + } + + /// Returns a raw pointer to the object. + /// + /// The pointer is valid for at least as long as the `Id` is held. + #[inline] + pub fn as_ptr(&self) -> *mut T { + // Note: This is not an associated function, which breaks the + // guideline that smart pointers shouldn't add inherent methods! + // + // However, this method is quite useful when migrating old codebases, + // so I think we'll let it be here for now. + self.ptr.as_ptr() + } } // TODO: Add ?Sized bound @@ -224,6 +251,21 @@ impl Id { unsafe { Self::new(res) } } + /// Retains an object pointer that may be null. + /// + /// This is just a convenience wrapper over [`Id::retain`] so that you + /// don't need to construct a [`NonNull`] when you know the pointer may + /// be null. + /// + /// # Safety + /// + /// Same as [`Id::retain`]. + #[inline] + pub unsafe fn retain_null(ptr: *mut T) -> Option> { + // SAFETY: Upheld by the caller + NonNull::new(ptr).map(|ptr| unsafe { Id::retain(ptr) }) + } + #[cfg_attr(not(debug_assertions), inline)] fn autorelease_inner(self) -> *mut T { // Note that this (and the actual `autorelease`) is not an associated @@ -232,7 +274,7 @@ impl Id { // retained objects it is hard to imagine a case where the inner type // has a method with the same name. - let ptr = ManuallyDrop::new(self).ptr.as_ptr() as *mut ffi::objc_object; + let ptr = ManuallyDrop::new(self).as_ptr() as *mut ffi::objc_object; // SAFETY: The `ptr` is guaranteed to be valid and have at least one // retain count. // And because of the ManuallyDrop, we don't call the Drop diff --git a/objc2/src/rc/weak_id.rs b/objc2/src/rc/weak_id.rs index de013a153..e739d0507 100644 --- a/objc2/src/rc/weak_id.rs +++ b/objc2/src/rc/weak_id.rs @@ -40,7 +40,7 @@ impl WeakId { // allow loading an `Id` later on. // SAFETY: `obj` is valid - unsafe { Self::new_inner(&**obj as *const T as *mut T) } + unsafe { Self::new_inner(obj.as_ptr()) } } /// # Safety diff --git a/objc2/src/runtime.rs b/objc2/src/runtime.rs index 84c4bba16..3a452b9cd 100644 --- a/objc2/src/runtime.rs +++ b/objc2/src/runtime.rs @@ -309,7 +309,7 @@ impl Class { #[allow(unused)] fn is_metaclass(&self) -> bool { - unsafe { Bool::from_raw(ffi::class_isMetaClass(self.as_ptr())).is_true() } + unsafe { Bool::from_raw(ffi::class_isMetaClass(self.as_ptr())).as_bool() } } /// Returns the size of instances of self. @@ -378,7 +378,7 @@ impl Class { /// Checks whether this class conforms to the specified protocol. pub fn conforms_to(&self, proto: &Protocol) -> bool { unsafe { - Bool::from_raw(ffi::class_conformsToProtocol(self.as_ptr(), proto.as_ptr())).is_true() + Bool::from_raw(ffi::class_conformsToProtocol(self.as_ptr(), proto.as_ptr())).as_bool() } } @@ -478,7 +478,7 @@ impl Protocol { self.as_ptr(), proto.as_ptr(), )) - .is_true() + .as_bool() } } @@ -493,7 +493,7 @@ impl PartialEq for Protocol { /// Check whether the protocols are equal, or conform to each other. #[inline] fn eq(&self, other: &Protocol) -> bool { - unsafe { Bool::from_raw(ffi::protocol_isEqual(self.as_ptr(), other.as_ptr())).is_true() } + unsafe { Bool::from_raw(ffi::protocol_isEqual(self.as_ptr(), other.as_ptr())).as_bool() } } }