Skip to content

Reference type as associated value instead of generic #132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jan 25, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion core-foundation-sys/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name = "core-foundation-sys"
description = "Bindings to Core Foundation for OS X"
homepage = "https://github.com/servo/core-foundation-rs"
repository = "https://github.com/servo/core-foundation-rs"
version = "0.4.6"
version = "0.5.0"
authors = ["The Servo Project Developers"]
license = "MIT / Apache-2.0"
build = "build.rs"
Expand Down
17 changes: 17 additions & 0 deletions core-foundation-sys/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,23 @@ pub struct CFAllocatorContext {
pub preferredSize: CFAllocatorPreferredSizeCallBack
}

/// Trait for all types which are Core Foundation reference types.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this commit should be reordered to come before 3688803.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. With "this commit" I assume you mean 0fc6b62 ? If so, it does come before 3688803

pub trait TCFTypeRef {
fn as_void_ptr(&self) -> *const c_void;

unsafe fn from_void_ptr(ptr: *const c_void) -> Self;
}

impl<T> TCFTypeRef for *const T {
fn as_void_ptr(&self) -> *const c_void {
(*self) as *const c_void
}

unsafe fn from_void_ptr(ptr: *const c_void) -> Self {
ptr as *const T
}
}

extern {
/*
* CFBase.h
Expand Down
2 changes: 1 addition & 1 deletion core-foundation-sys/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use string::CFStringRef;
#[repr(C)]
pub struct __CFError(c_void);

pub type CFErrorRef = *mut __CFError;
pub type CFErrorRef = *const __CFError;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if this was *mut for a reason I have missed. But it was the only CF-ref typedef which was not *const so my guess is it was a typo. All tests of this crate and all dependant crates I tried work fine after this change.


extern "C" {
pub fn CFErrorGetTypeID() -> CFTypeID;
Expand Down
4 changes: 2 additions & 2 deletions core-foundation/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@ name = "core-foundation"
description = "Bindings to Core Foundation for OS X"
homepage = "https://github.com/servo/core-foundation-rs"
repository = "https://github.com/servo/core-foundation-rs"
version = "0.4.6"
version = "0.5.0"
authors = ["The Servo Project Developers"]
license = "MIT / Apache-2.0"

[dependencies.core-foundation-sys]
path = "../core-foundation-sys"
version = "0.4.6"
version = "0.5.0"

[dependencies]
libc = "0.2"
Expand Down
33 changes: 21 additions & 12 deletions core-foundation/src/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ impl_CFTypeDescriptionGeneric!(CFArray);

impl<T> CFArray<T> {
/// Creates a new `CFArray` with the given elements, which must be `CFType` objects.
pub fn from_CFTypes<R>(elems: &[T]) -> CFArray<T> where T: TCFType<R> {
pub fn from_CFTypes(elems: &[T]) -> CFArray<T> where T: TCFType {
unsafe {
let elems: Vec<CFTypeRef> = elems.iter().map(|elem| elem.as_CFTypeRef()).collect();
let array_ref = CFArrayCreate(kCFAllocatorDefault,
Expand All @@ -92,13 +92,18 @@ impl<T> CFArray<T> {
}
}

#[deprecated(note = "please use `as_untyped` instead")]
pub fn to_untyped(self) -> CFArray {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize now that my previous move to deprecate to_untyped and add as_untyped was not exactly making things more correct than before, if one should follow the API guidelines to the letter.
This removed to_untyped is a consuming convert (owned -> owned (non-Copy types)), so it should have been named into_untyped with that signature. So the old name was slightly "wrong".
On the other hand, the new as_untyped only borrows &self, but gives out an owned object (borrowed -> owned (non-Copy types)), so it should be named to_untyped, and not as_ since that is for conversions borrowed -> borrowed.

Do you think I should change the name of the method that will be present in the 0.5.0 release? Or is that too much nitpicking and too much changing the API back and forth? If it should be changed this would be the correct signature:

pub fn to_untyped(&self) -> CFArray { ... }

In line with all the new downcast_into methods, we could also add a consuming one that avoids bumping the retain count:

pub fn into_untyped(self) -> CFArray { ... }

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Those seem like worthwhile changes.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Check. I'll add a commit for that. I'll push it after my other PR is merged, as this one will need rebasing/manual conflict resolution after that one anyway.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

#[inline]
pub fn to_untyped(&self) -> CFArray {
unsafe { CFArray::wrap_under_get_rule(self.0) }
}

pub fn as_untyped(&self) -> CFArray {
unsafe { CFArray::wrap_under_get_rule(self.0) }
/// Returns the same array, but with the type reset to void pointers.
/// Equal to `to_untyped`, but is faster since it does not increment the retain count.
#[inline]
pub fn into_untyped(self) -> CFArray {
let reference = self.0;
mem::forget(self);
unsafe { CFArray::wrap_under_create_rule(reference) }
}

/// Iterates over the elements of this `CFArray`.
Expand Down Expand Up @@ -164,19 +169,23 @@ mod tests {
assert_eq!(array.retain_count(), 1);

let untyped_array = array.to_untyped();
assert_eq!(array.retain_count(), 2);
assert_eq!(untyped_array.retain_count(), 2);

mem::drop(array);
assert_eq!(untyped_array.retain_count(), 1);
}

#[test]
fn as_untyped_correct_retain_count() {
fn into_untyped() {
let array = CFArray::<CFType>::from_CFTypes(&[]);
assert_eq!(array.retain_count(), 1);

let untyped_array = array.as_untyped();
let array2 = array.to_untyped();
assert_eq!(array.retain_count(), 2);

let untyped_array = array.into_untyped();
assert_eq!(untyped_array.retain_count(), 2);

mem::drop(array);
mem::drop(array2);
assert_eq!(untyped_array.retain_count(), 1);
}

Expand Down Expand Up @@ -215,14 +224,14 @@ mod tests {
assert_eq!(iter.len(), 5);

for elem in iter {
let number: CFNumber = elem.downcast::<_, CFNumber>().unwrap();
let number: CFNumber = elem.downcast::<CFNumber>().unwrap();
sum += number.to_i64().unwrap()
}

assert!(sum == 15);

for elem in arr.iter() {
let number: CFNumber = elem.downcast::<_, CFNumber>().unwrap();
let number: CFNumber = elem.downcast::<CFNumber>().unwrap();
sum += number.to_i64().unwrap()
}

Expand Down
56 changes: 33 additions & 23 deletions core-foundation/src/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,18 +53,19 @@ impl CFType {
/// // Cast it up to a CFType.
/// let cf_type: CFType = string.as_CFType();
/// // Cast it down again.
/// assert!(cf_type.downcast::<_, CFString>().unwrap().to_string() == "FooBar");
/// assert!(cf_type.downcast::<CFString>().unwrap().to_string() == "FooBar");
/// // Casting it to some other type will yield `None`
/// assert!(cf_type.downcast::<_, CFBoolean>().is_none());
/// assert!(cf_type.downcast::<CFBoolean>().is_none());
/// ```
///
/// [`Box::downcast`]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast
/// [`CFPropertyList::downcast`]: ../propertylist/struct.CFPropertyList.html#method.downcast
#[inline]
pub fn downcast<Raw, T: TCFType<*const Raw>>(&self) -> Option<T> {
if self.instance_of::<_, T>() {
pub fn downcast<T: TCFType>(&self) -> Option<T> {
if self.instance_of::<T>() {
unsafe {
Some(T::wrap_under_get_rule(self.0 as *const Raw))
let reference = T::Ref::from_void_ptr(self.0);
Some(T::wrap_under_get_rule(reference))
}
} else {
None
Expand All @@ -75,11 +76,13 @@ impl CFType {
///
/// [`downcast`]: #method.downcast
#[inline]
pub fn downcast_into<Raw, T: TCFType<*const Raw>>(self) -> Option<T> {
if self.instance_of::<_, T>() {
let reference = self.0 as *const Raw;
mem::forget(self);
unsafe { Some(T::wrap_under_create_rule(reference)) }
pub fn downcast_into<T: TCFType>(self) -> Option<T> {
if self.instance_of::<T>() {
unsafe {
let reference = T::Ref::from_void_ptr(self.0);
mem::forget(self);
Some(T::wrap_under_create_rule(reference))
}
} else {
None
}
Expand Down Expand Up @@ -126,16 +129,20 @@ impl CFAllocator {
}
}

/// All Core Foundation types implement this trait. The type parameter `TypeRef` specifies the

/// All Core Foundation types implement this trait. The associated type `Ref` specifies the
/// associated Core Foundation type: e.g. for `CFType` this is `CFTypeRef`; for `CFArray` this is
/// `CFArrayRef`.
pub trait TCFType<ConcreteTypeRef> {
pub trait TCFType {
/// The reference type wrapped inside this type.
type Ref: TCFTypeRef;

/// Returns the object as its concrete TypeRef.
fn as_concrete_TypeRef(&self) -> ConcreteTypeRef;
fn as_concrete_TypeRef(&self) -> Self::Ref;

/// Returns an instance of the object, wrapping the underlying `CFTypeRef` subclass. Use this
/// when following Core Foundation's "Create Rule". The reference count is *not* bumped.
unsafe fn wrap_under_create_rule(obj: ConcreteTypeRef) -> Self;
unsafe fn wrap_under_create_rule(obj: Self::Ref) -> Self;

/// Returns the type ID for this class.
fn type_id() -> CFTypeID;
Expand All @@ -152,7 +159,8 @@ pub trait TCFType<ConcreteTypeRef> {
/// count.
#[inline]
fn into_CFType(self) -> CFType
where Self: Sized
where
Self: Sized,
{
let reference = self.as_CFTypeRef();
mem::forget(self);
Expand All @@ -164,7 +172,7 @@ pub trait TCFType<ConcreteTypeRef> {

/// Returns an instance of the object, wrapping the underlying `CFTypeRef` subclass. Use this
/// when following Core Foundation's "Get Rule". The reference count *is* bumped.
unsafe fn wrap_under_get_rule(reference: ConcreteTypeRef) -> Self;
unsafe fn wrap_under_get_rule(reference: Self::Ref) -> Self;

/// Returns the reference count of the object. It is unwise to do anything other than test
/// whether the return value of this method is greater than zero.
Expand Down Expand Up @@ -192,12 +200,14 @@ pub trait TCFType<ConcreteTypeRef> {

/// Returns true if this value is an instance of another type.
#[inline]
fn instance_of<OtherConcreteTypeRef,OtherCFType:TCFType<OtherConcreteTypeRef>>(&self) -> bool {
self.type_of() == <OtherCFType as TCFType<_>>::type_id()
fn instance_of<OtherCFType: TCFType>(&self) -> bool {
self.type_of() == OtherCFType::type_id()
}
}

impl TCFType<CFTypeRef> for CFType {
impl TCFType for CFType {
type Ref = CFTypeRef;

#[inline]
fn as_concrete_TypeRef(&self) -> CFTypeRef {
self.0
Expand Down Expand Up @@ -238,8 +248,8 @@ mod tests {
let string = CFString::from_static_string("foo");
let cftype = string.as_CFType();

assert!(cftype.instance_of::<_, CFString>());
assert!(!cftype.instance_of::<_, CFBoolean>());
assert!(cftype.instance_of::<CFString>());
assert!(!cftype.instance_of::<CFBoolean>());
}

#[test]
Expand All @@ -264,7 +274,7 @@ mod tests {
fn as_cftype_and_downcast() {
let string = CFString::from_static_string("bar");
let cftype = string.as_CFType();
let string2 = cftype.downcast::<_, CFString>().unwrap();
let string2 = cftype.downcast::<CFString>().unwrap();
assert_eq!(string2.to_string(), "bar");

assert_eq!(string.retain_count(), 3);
Expand All @@ -276,7 +286,7 @@ mod tests {
fn into_cftype_and_downcast_into() {
let string = CFString::from_static_string("bar");
let cftype = string.into_CFType();
let string2 = cftype.downcast_into::<_, CFString>().unwrap();
let string2 = cftype.downcast_into::<CFString>().unwrap();
assert_eq!(string2.to_string(), "bar");
assert_eq!(string2.retain_count(), 1);
}
Expand Down
Loading