Skip to content

Commit

Permalink
Merge pull request #158 from madsmtm/foundation-traits
Browse files Browse the repository at this point in the history
Implement `AsRef`/`AsMut` that forward to objects themselves.
Implement `Borrow`/`BorrowMut` for objects.
Implement `ToOwned` using `NSCopying`/`NSMutableCopying` where appropriate.
  • Loading branch information
madsmtm authored Jun 11, 2022
2 parents 378fcea + 5f5ff6c commit 42b847a
Show file tree
Hide file tree
Showing 11 changed files with 189 additions and 11 deletions.
2 changes: 2 additions & 0 deletions objc2-foundation/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
* Added `NSPoint`.
* Added `NSSize`.
* Added `NSRect`.
* Implement `Borrow` and `BorrowMut` for all objects.
* Implement `ToOwned` for copyable types.

### Changed
* **BREAKING**: Removed the following helper traits in favor of inherent
Expand Down
14 changes: 14 additions & 0 deletions objc2-foundation/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,13 @@ unsafe impl<T: Message> NSMutableCopying for NSArray<T, Shared> {
type Output = NSMutableArray<T, Shared>;
}

impl<T: Message> alloc::borrow::ToOwned for NSArray<T, Shared> {
type Owned = Id<NSArray<T, Shared>, Shared>;
fn to_owned(&self) -> Self::Owned {
self.copy()
}
}

unsafe impl<T: Message, O: Ownership> NSFastEnumeration for NSArray<T, O> {
type Item = T;
}
Expand Down Expand Up @@ -334,6 +341,13 @@ unsafe impl<T: Message> NSMutableCopying for NSMutableArray<T, Shared> {
type Output = NSMutableArray<T, Shared>;
}

impl<T: Message> alloc::borrow::ToOwned for NSMutableArray<T, Shared> {
type Owned = Id<NSMutableArray<T, Shared>, Owned>;
fn to_owned(&self) -> Self::Owned {
self.mutable_copy()
}
}

unsafe impl<T: Message, O: Ownership> NSFastEnumeration for NSMutableArray<T, O> {
type Item = T;
}
Expand Down
7 changes: 7 additions & 0 deletions objc2-foundation/src/attributed_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,13 @@ unsafe impl NSMutableCopying for NSAttributedString {
type Output = NSMutableAttributedString;
}

impl alloc::borrow::ToOwned for NSAttributedString {
type Owned = Id<NSAttributedString, Shared>;
fn to_owned(&self) -> Self::Owned {
self.copy()
}
}

#[cfg(test)]
mod tests {
use alloc::string::ToString;
Expand Down
61 changes: 60 additions & 1 deletion objc2-foundation/src/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,12 +92,22 @@ unsafe impl NSMutableCopying for NSData {
type Output = NSMutableData;
}

impl alloc::borrow::ToOwned for NSData {
type Owned = Id<NSData, Shared>;
fn to_owned(&self) -> Self::Owned {
self.copy()
}
}

impl AsRef<[u8]> for NSData {
fn as_ref(&self) -> &[u8] {
self.bytes()
}
}

// Note: We don't implement `Borrow<[u8]>` since we can't guarantee that `Eq`,
// `Ord` and `Hash` are equal for `NSData` vs. `[u8]`!

impl<I: SliceIndex<[u8]>> Index<I> for NSData {
type Output = I::Output;

Expand Down Expand Up @@ -217,6 +227,13 @@ unsafe impl NSMutableCopying for NSMutableData {
type Output = NSMutableData;
}

impl alloc::borrow::ToOwned for NSMutableData {
type Owned = Id<NSMutableData, Owned>;
fn to_owned(&self) -> Self::Owned {
self.mutable_copy()
}
}

impl AsRef<[u8]> for NSMutableData {
fn as_ref(&self) -> &[u8] {
self.bytes()
Expand Down Expand Up @@ -336,7 +353,7 @@ unsafe fn data_from_vec(cls: &Class, bytes: Vec<u8>) -> *mut Object {

#[cfg(test)]
mod tests {
use super::{NSData, NSMutableData};
use super::*;
#[cfg(feature = "block")]
use alloc::vec;

Expand Down Expand Up @@ -431,4 +448,46 @@ mod tests {
data.extend(iter);
assert_eq!(data.bytes(), &[1, 2, 3, 4, 5]);
}

#[test]
fn test_as_ref_borrow() {
use core::borrow::{Borrow, BorrowMut};

fn impls_borrow<T: AsRef<U> + Borrow<U> + ?Sized, U: ?Sized>(_: &T) {}
fn impls_borrow_mut<T: AsMut<U> + BorrowMut<U> + ?Sized, U: ?Sized>(_: &mut T) {}

let mut obj = NSMutableData::new();
impls_borrow::<Id<NSMutableData, Owned>, NSMutableData>(&obj);
impls_borrow_mut::<Id<NSMutableData, Owned>, NSMutableData>(&mut obj);

impls_borrow::<NSMutableData, NSMutableData>(&obj);
impls_borrow_mut::<NSMutableData, NSMutableData>(&mut obj);
impls_borrow::<NSMutableData, NSData>(&obj);
impls_borrow_mut::<NSMutableData, NSData>(&mut obj);
impls_borrow::<NSMutableData, NSObject>(&obj);
impls_borrow_mut::<NSMutableData, NSObject>(&mut obj);
impls_borrow::<NSMutableData, Object>(&obj);
impls_borrow_mut::<NSMutableData, Object>(&mut obj);

impls_borrow::<NSData, NSData>(&obj);
impls_borrow_mut::<NSData, NSData>(&mut obj);
impls_borrow::<NSData, NSObject>(&obj);
impls_borrow_mut::<NSData, NSObject>(&mut obj);
impls_borrow::<NSData, Object>(&obj);
impls_borrow_mut::<NSData, Object>(&mut obj);

fn impls_as_ref<T: AsRef<U> + ?Sized, U: ?Sized>(_: &T) {}
fn impls_as_mut<T: AsMut<U> + ?Sized, U: ?Sized>(_: &mut T) {}

impls_as_ref::<NSMutableData, [u8]>(&obj);
impls_as_mut::<NSMutableData, [u8]>(&mut obj);
impls_as_ref::<NSData, [u8]>(&obj);

let obj: &mut NSMutableData = &mut *obj;
let _: &[u8] = obj.as_ref();
let _: &mut [u8] = obj.as_mut();

let obj: &mut NSData = &mut **obj;
let _: &[u8] = obj.as_ref();
}
}
51 changes: 44 additions & 7 deletions objc2-foundation/src/macros.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,45 @@
macro_rules! __impl_as_ref_as_mut {
macro_rules! __impl_as_ref_borrow {
($name:ident<$($t:ident $(: $b:ident)?),*>,) => {};
($name:ident<$($t:ident $(: $b:ident)?),*>, $item:ty, $($tail:ty,)*) => {
impl<$($t $(: $b)?),*> AsRef<$item> for $name<$($t),*> {
#[inline]
fn as_ref(&self) -> &$item {
&**self
// Triggers Deref coercion depending on return type
&*self
}
}

impl<$($t $(: $b)?),*> AsMut<$item> for $name<$($t),*> {
#[inline]
fn as_mut(&mut self) -> &mut $item {
&mut **self
// Triggers DerefMut coercion depending on return type
&mut *self
}
}

__impl_as_ref_as_mut!($name<$($t $(: $b)?),*>, $($tail,)*);
// Borrow and BorrowMut are correct, since subclasses behaves
// identical to the class they inherit (message sending doesn't care).
//
// In particular, `Eq`, `Ord` and `Hash` all give the same results
// after borrow.

impl<$($t $(: $b)?),*> ::core::borrow::Borrow<$item> for $name<$($t),*> {
#[inline]
fn borrow(&self) -> &$item {
// Triggers Deref coercion depending on return type
&*self
}
}

impl<$($t $(: $b)?),*> ::core::borrow::BorrowMut<$item> for $name<$($t),*> {
#[inline]
fn borrow_mut(&mut self) -> &mut $item {
// Triggers Deref coercion depending on return type
&mut *self
}
}

__impl_as_ref_borrow!($name<$($t $(: $b)?),*>, $($tail,)*);
};
}

Expand Down Expand Up @@ -125,7 +149,21 @@ macro_rules! object {
}
}

__impl_as_ref_as_mut!($name<$($t $(: $b)?),*>, $inherits, $($inheritance_rest,)*);
impl<$($t $(: $b)?),*> AsRef<Self> for $name<$($t),*> {
#[inline]
fn as_ref(&self) -> &Self {
self
}
}

impl<$($t $(: $b)?),*> AsMut<Self> for $name<$($t),*> {
#[inline]
fn as_mut(&mut self) -> &mut Self {
self
}
}

__impl_as_ref_borrow!($name<$($t $(: $b)?),*>, $inherits, $($inheritance_rest,)*);

// Objective-C equality has approximately the same semantics as Rust
// equality (although less aptly specified).
Expand Down Expand Up @@ -166,13 +204,12 @@ macro_rules! object {
// TODO: Consider T: Debug bound
impl<$($t $(: $b)?),*> ::core::fmt::Debug for $name<$($t),*> {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
use ::objc2::MessageReceiver;
use ::alloc::borrow::ToOwned;
use $crate::NSObject;
// "downgrading" to NSObject and calling `to_owned` to work
// around `f` and Self not being AutoreleaseSafe.
// TODO: Fix this!
let this: &NSObject = unsafe { &*self.as_raw_receiver().cast() };
let this: &NSObject = self.as_ref();
let s = ::objc2::rc::autoreleasepool(|pool| {
this.description().as_str(pool).to_owned()
});
Expand Down
7 changes: 7 additions & 0 deletions objc2-foundation/src/mutable_attributed_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,13 @@ unsafe impl NSMutableCopying for NSMutableAttributedString {
type Output = NSMutableAttributedString;
}

impl alloc::borrow::ToOwned for NSMutableAttributedString {
type Owned = Id<NSMutableAttributedString, Owned>;
fn to_owned(&self) -> Self::Owned {
self.mutable_copy()
}
}

#[cfg(test)]
mod tests {
use alloc::string::ToString;
Expand Down
7 changes: 7 additions & 0 deletions objc2-foundation/src/mutable_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@ unsafe impl NSMutableCopying for NSMutableString {
type Output = NSMutableString;
}

impl alloc::borrow::ToOwned for NSMutableString {
type Owned = Id<NSMutableString, Owned>;
fn to_owned(&self) -> Self::Owned {
self.mutable_copy()
}
}

impl AddAssign<&NSString> for NSMutableString {
#[inline]
fn add_assign(&mut self, other: &NSString) {
Expand Down
28 changes: 26 additions & 2 deletions objc2-foundation/src/object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,34 @@ impl DefaultId for NSObject {

#[cfg(test)]
mod tests {
use super::NSObject;
use crate::NSString;
use super::*;
use alloc::format;

#[test]
fn test_deref() {
let mut obj: Id<NSObject, Owned> = NSObject::new();
let _: &NSObject = &*obj;
let _: &mut NSObject = &mut *obj;
let _: &Object = &*obj;
let _: &mut Object = &mut *obj;
}

#[test]
fn test_as_ref_borrow() {
use core::borrow::{Borrow, BorrowMut};

fn impls_as_ref<T: AsRef<U> + Borrow<U> + ?Sized, U: ?Sized>(_: &T) {}
fn impls_as_mut<T: AsMut<U> + BorrowMut<U> + ?Sized, U: ?Sized>(_: &mut T) {}

let mut obj = NSObject::new();
impls_as_ref::<Id<NSObject, Owned>, NSObject>(&obj);
impls_as_mut::<Id<NSObject, Owned>, NSObject>(&mut obj);
impls_as_ref::<NSObject, NSObject>(&obj);
impls_as_mut::<NSObject, NSObject>(&mut obj);
impls_as_ref::<NSObject, Object>(&obj);
impls_as_mut::<NSObject, Object>(&mut obj);
}

#[test]
fn test_equality() {
let obj1 = NSObject::new();
Expand Down
9 changes: 8 additions & 1 deletion objc2-foundation/src/string.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use alloc::borrow::ToOwned;
use core::cmp;
use core::ffi::c_void;
use core::fmt;
Expand All @@ -6,7 +7,6 @@ use core::slice;
use core::str;
use std::os::raw::c_char;

use alloc::borrow::ToOwned;
use objc2::ffi;
use objc2::rc::DefaultId;
use objc2::rc::{autoreleasepool, AutoreleasePool};
Expand Down Expand Up @@ -255,6 +255,13 @@ unsafe impl NSMutableCopying for NSString {
type Output = NSMutableString;
}

impl ToOwned for NSString {
type Owned = Id<NSString, Shared>;
fn to_owned(&self) -> Self::Owned {
self.copy()
}
}

impl fmt::Display for NSString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// The call to `to_owned` is unfortunate, but is required to work
Expand Down
7 changes: 7 additions & 0 deletions objc2-foundation/src/uuid.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,13 @@ unsafe impl NSCopying for NSUUID {
type Output = NSUUID;
}

impl alloc::borrow::ToOwned for NSUUID {
type Owned = Id<NSUUID, Shared>;
fn to_owned(&self) -> Self::Owned {
self.copy()
}
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
7 changes: 7 additions & 0 deletions objc2-foundation/src/value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ unsafe impl<T: 'static> NSCopying for NSValue<T> {
type Output = NSValue<T>;
}

impl<T: 'static> alloc::borrow::ToOwned for NSValue<T> {
type Owned = Id<NSValue<T>, Shared>;
fn to_owned(&self) -> Self::Owned {
self.copy()
}
}

impl<T: 'static + Copy + Encode + Ord> Ord for NSValue<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.get().cmp(&other.get())
Expand Down

0 comments on commit 42b847a

Please sign in to comment.