diff --git a/objc2-foundation/CHANGELOG.md b/objc2-foundation/CHANGELOG.md index 33b08fa74..6f23ac412 100644 --- a/objc2-foundation/CHANGELOG.md +++ b/objc2-foundation/CHANGELOG.md @@ -10,8 +10,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `MainThreadMarker` to help with designing APIs where a method is only safe to call on the main thread. * Added `NSException` object. -* Added `extern_class!` macro to help with defining other classes. +* Added `extern_class!` macro to help with defining interfaces to classes. +* Added `declare_class!` macro to help with declaring custom classes. * Expose the `objc2` version that this uses in the crate root. +* Added `NSZone`. ### Changed * Changed a few `Debug` impls. diff --git a/objc2-foundation/examples/custom_class.rs b/objc2-foundation/examples/custom_class.rs index bb5319fdf..53ddf80a7 100644 --- a/objc2-foundation/examples/custom_class.rs +++ b/objc2-foundation/examples/custom_class.rs @@ -1,17 +1,22 @@ use std::sync::Once; -use objc2::declare::ClassBuilder; +use objc2::declare::{ClassBuilder, Ivar, IvarType}; use objc2::rc::{Id, Owned}; use objc2::runtime::{Class, Object, Sel}; use objc2::{msg_send, msg_send_id, sel}; use objc2::{Encoding, Message, RefEncode}; use objc2_foundation::NSObject; -/// In the future this should be an `extern type`, if that gets stabilized, -/// see [RFC-1861](https://rust-lang.github.io/rfcs/1861-extern-types.html). +struct NumberIvar; +unsafe impl IvarType for NumberIvar { + type Type = u32; + const NAME: &'static str = "_number"; +} + #[repr(C)] pub struct MYObject { inner: Object, + number: Ivar, } unsafe impl RefEncode for MYObject { @@ -28,27 +33,19 @@ impl MYObject { unsafe { msg_send_id![cls, new].unwrap() } } - fn number(&self) -> u32 { - unsafe { *self.inner.ivar("_number") } - } - - fn set_number(&mut self, number: u32) { - unsafe { self.inner.set_ivar("_number", number) }; - } - fn class() -> &'static Class { MYOBJECT_REGISTER_CLASS.call_once(|| { let superclass = NSObject::class(); let mut builder = ClassBuilder::new("MYObject", superclass).unwrap(); - builder.add_ivar::("_number"); + builder.add_ivar::<::Type>(::NAME); // Add ObjC methods for getting and setting the number extern "C" fn my_object_set_number(this: &mut MYObject, _cmd: Sel, number: u32) { - this.set_number(number); + *this.number = number; } extern "C" fn my_object_get_number(this: &MYObject, _cmd: Sel) -> u32 { - this.number() + *this.number } unsafe { @@ -68,7 +65,7 @@ impl MYObject { fn main() { let mut obj = MYObject::new(); - obj.set_number(7); + *obj.number = 7; println!("Number: {}", unsafe { let number: u32 = msg_send![&obj, number]; number @@ -77,5 +74,5 @@ fn main() { unsafe { let _: () = msg_send![&mut obj, setNumber: 12u32]; } - println!("Number: {}", obj.number()); + println!("Number: {}", obj.number); } diff --git a/objc2-foundation/examples/declaration.rs b/objc2-foundation/examples/declaration.rs new file mode 100644 index 000000000..2f1ebdbf9 --- /dev/null +++ b/objc2-foundation/examples/declaration.rs @@ -0,0 +1,83 @@ +use objc2::{ + msg_send, msg_send_id, + rc::{Id, Shared}, + runtime::{Bool, Object}, +}; +use objc2_foundation::{declare_class, extern_class, NSObject}; + +#[cfg(all(feature = "apple", target_os = "macos"))] +#[link(name = "AppKit", kind = "framework")] +extern "C" {} + +extern_class! { + unsafe struct NSResponder: NSObject; +} + +declare_class! { + unsafe struct CustomAppDelegate: NSResponder, NSObject { + pub ivar: u8, + another_ivar: Bool, + } + + unsafe impl { + @sel(initWith:another:) + fn init_with( + self: &mut Self, + ivar: u8, + another_ivar: Bool, + ) -> *mut Self { + let this: *mut Self = unsafe { + msg_send![super(self, NSResponder::class()), init] + }; + if let Some(this) = unsafe { this.as_mut() } { + // TODO: Allow initialization through MaybeUninit + *this.ivar = ivar; + *this.another_ivar = another_ivar; + } + this + } + + @sel(myClassMethod) + fn my_class_method() { + println!("A class method!"); + } + } + + // For some reason, `NSApplicationDelegate` is not a "real" protocol we + // can retrieve using `objc_getProtocol` - it seems it is created by + // `clang` only when used in Objective-C... + // + // TODO: Investigate this! + unsafe impl { + @sel(applicationDidFinishLaunching:) + fn did_finish_launching(&self, _sender: *mut Object) { + println!("Did finish launching!"); + } + + @sel(applicationWillTerminate:) + fn will_terminate(&self, _: *mut Object) { + println!("Will terminate!"); + } + } +} + +impl CustomAppDelegate { + pub fn new(ivar: u8, another_ivar: bool) -> Id { + let cls = Self::class(); + unsafe { + msg_send_id![ + msg_send_id![cls, alloc], + initWith: ivar, + another: Bool::from(another_ivar), + ] + .unwrap() + } + } +} + +fn main() { + let delegate = CustomAppDelegate::new(42, true); + + println!("{}", delegate.ivar); + println!("{}", delegate.another_ivar.as_bool()); +} diff --git a/objc2-foundation/src/declare_macro.rs b/objc2-foundation/src/declare_macro.rs new file mode 100644 index 000000000..d3c9beb97 --- /dev/null +++ b/objc2-foundation/src/declare_macro.rs @@ -0,0 +1,756 @@ +#[doc(hidden)] +#[macro_export] +macro_rules! __inner_declare_class { + {@rewrite_methods @$output_type:ident @$builder:ident} => {}; + { + @rewrite_methods + @$output_type:ident + @$builder:ident + + $(#[$m:meta])* + @sel($($sel:tt)+) + fn $name:ident($($args:tt)*) $(-> $ret:ty)? $body:block + + $($rest:tt)* + } => { + $crate::__inner_declare_class! { + @rewrite_methods_inner + @$output_type + @$builder + // Split args out so that we can match on `self`, while still use + // it as a function argument + ($($args)*) + + $(#[$m])* + @sel($($sel)+) + fn $name($($args)*) $(-> $ret)? $body + } + + $crate::__inner_declare_class! { + @rewrite_methods + @$output_type + @$builder + + $($rest)* + } + }; + + // Instance method + { + @rewrite_methods_inner + @$output_type:ident + @$builder:ident + (&mut self $($__rest_args:tt)*) + + $(#[$m:meta])* + @sel($($sel:tt)+) + fn $name:ident( + &mut $self:ident + $(, $($rest_args:tt)*)? + ) $(-> $ret:ty)? $body:block + } => { + $crate::__inner_declare_class! { + @$output_type + @instance_method + @sel($($sel)*) + @$name + @$builder + @($($($rest_args)*)?) + + $(#[$m])* + extern "C" fn $name( + &mut $self, + _: $crate::objc2::runtime::Sel, + $($($rest_args)*)? + ) $(-> $ret)? $body + } + }; + { + @rewrite_methods_inner + @$output_type:ident + @$builder:ident + (&self $($__rest_args:tt)*) + + $(#[$m:meta])* + @sel($($sel:tt)+) + fn $name:ident( + &$self:ident + $(, $($rest_args:tt)*)? + ) $(-> $ret:ty)? $body:block + } => { + $crate::__inner_declare_class! { + @$output_type + @instance_method + @sel($($sel)*) + @$name + @$builder + @($($($rest_args)*)?) + + $(#[$m])* + extern "C" fn $name( + &$self, + _: $crate::objc2::runtime::Sel, + $($($rest_args)*)? + ) $(-> $ret)? $body + } + }; + { + @rewrite_methods_inner + @$output_type:ident + @$builder:ident + ( + mut self: $__self_ty:ty + $(, $($__rest_args:tt)*)? + ) + + $(#[$m:meta])* + @sel($($sel:tt)+) + fn $name:ident( + mut $self:ident: $self_ty:ty + $(, $($rest_args:tt)*)? + ) $(-> $ret:ty)? $body:block + } => { + $crate::__inner_declare_class! { + @$output_type + @instance_method + @sel($($sel)*) + @$name + @$builder + @($($($rest_args)*)?) + + $(#[$m])* + extern "C" fn $name( + mut $self: $self_ty, + _: $crate::objc2::runtime::Sel, + $($($rest_args)*)? + ) $(-> $ret)? $body + } + }; + { + @rewrite_methods_inner + @$output_type:ident + @$builder:ident + ( + self: $__self_ty:ty + $(, $($__rest_args:tt)*)? + ) + + $(#[$m:meta])* + @sel($($sel:tt)+) + fn $name:ident( + $self:ident: $self_ty:ty + $(, $($rest_args:tt)*)? + ) $(-> $ret:ty)? $body:block + } => { + $crate::__inner_declare_class! { + @$output_type + @instance_method + @sel($($sel)*) + @$name + @$builder + @($($($rest_args)*)?) + + $(#[$m])* + extern "C" fn $name( + $self: $self_ty, + _: $crate::objc2::runtime::Sel, + $($($rest_args)*)? + ) $(-> $ret)? $body + } + }; + + // Class method + { + @rewrite_methods_inner + @$output_type:ident + @$builder:ident + ($($__args:tt)*) + + $(#[$m:meta])* + @sel($($sel:tt)+) + fn $name:ident( + $($args:tt)* + ) $(-> $ret:ty)? $body:block + } => { + $crate::__inner_declare_class! { + @$output_type + @class_method + @sel($($sel)*) + @$name + @$builder + @($($args)*) + + $(#[$m])* + extern "C" fn $name( + _: &$crate::objc2::runtime::Class, + _: $crate::objc2::runtime::Sel, + $($args)* + ) $(-> $ret)? $body + } + }; + + { + @method_out + @$method_type:ident + @sel($($sel:tt)*) + @$name:ident + @$builder:ident + @($($builder_args:tt)*) + + $method:item + } => { + $method + }; + { + @register_out + @class_method + @sel($($sel:tt)*) + @$name:ident + @$builder:ident + @($($builder_args:tt)*) + + $method:item + } => { + $builder.add_class_method( + $crate::objc2::sel!($($sel)*), + $crate::__inner_declare_class! { + @cast_extern_fn + @$name + $($builder_args)* + }, + ); + }; + { + @register_out + @instance_method + @sel($($sel:tt)*) + @$name:ident + @$builder:ident + @($($builder_args:tt)*) + + $method:item + } => { + $builder.add_method( + $crate::objc2::sel!($($sel)*), + $crate::__inner_declare_class! { + @cast_extern_fn + @$name + $($builder_args)* + }, + ); + }; + + // Create the `as extern "C" fn(...) -> _` cast + // + // TODO: Investigate if there's a better way of doing this + { + @cast_extern_fn + @$name:ident + + $(,)? + } => { + Self::$name as extern "C" fn(_, _) -> _ + }; + { + @cast_extern_fn + @$name:ident + + $($param1:ident)? $(_)?: $param1_ty:ty $(,)? + } => { + Self::$name as extern "C" fn(_, _, _) -> _ + }; + { + @cast_extern_fn + @$name:ident + + $($param1:ident)? $(_)?: $param1_ty:ty, + $($param2:ident)? $(_)?: $param2_ty:ty $(,)? + } => { + Self::$name as extern "C" fn(_, _, _, _) -> _ + }; + { + @cast_extern_fn + @$name:ident + + $($param1:ident)? $(_)?: $param1_ty:ty, + $($param2:ident)? $(_)?: $param2_ty:ty, + $($param3:ident)? $(_)?: $param3_ty:ty $(,)? + } => { + Self::$name as extern "C" fn(_, _, _, _, _) -> _ + }; + { + @cast_extern_fn + @$name:ident + + $($param1:ident)? $(_)?: $param1_ty:ty, + $($param2:ident)? $(_)?: $param2_ty:ty, + $($param3:ident)? $(_)?: $param3_ty:ty, + $($param4:ident)? $(_)?: $param4_ty:ty $(,)? + } => { + Self::$name as extern "C" fn(_, _, _, _, _, _) -> _ + }; + { + @cast_extern_fn + @$name:ident + + $($param1:ident)? $(_)?: $param1_ty:ty, + $($param2:ident)? $(_)?: $param2_ty:ty, + $($param3:ident)? $(_)?: $param3_ty:ty, + $($param4:ident)? $(_)?: $param4_ty:ty, + $($param5:ident)? $(_)?: $param5_ty:ty $(,)? + } => { + Self::$name as extern "C" fn(_, _, _, _, _, _, _) -> _ + }; + { + @cast_extern_fn + @$name:ident + + $($param1:ident)? $(_)?: $param1_ty:ty, + $($param2:ident)? $(_)?: $param2_ty:ty, + $($param3:ident)? $(_)?: $param3_ty:ty, + $($param4:ident)? $(_)?: $param4_ty:ty, + $($param5:ident)? $(_)?: $param5_ty:ty, + $($param6:ident)? $(_)?: $param6_ty:ty $(,)? + } => { + Self::$name as extern "C" fn(_, _, _, _, _, _, _, _) -> _ + }; + { + @cast_extern_fn + @$name:ident + + $($param1:ident)? $(_)?: $param1_ty:ty, + $($param2:ident)? $(_)?: $param2_ty:ty, + $($param3:ident)? $(_)?: $param3_ty:ty, + $($param4:ident)? $(_)?: $param4_ty:ty, + $($param5:ident)? $(_)?: $param5_ty:ty, + $($param6:ident)? $(_)?: $param6_ty:ty, + $($param7:ident)? $(_)?: $param7_ty:ty $(,)? + } => { + Self::$name as extern "C" fn(_, _, _, _, _, _, _, _, _) -> _ + }; + { + @cast_extern_fn + @$name:ident + + $($param1:ident)? $(_)?: $param1_ty:ty, + $($param2:ident)? $(_)?: $param2_ty:ty, + $($param3:ident)? $(_)?: $param3_ty:ty, + $($param4:ident)? $(_)?: $param4_ty:ty, + $($param5:ident)? $(_)?: $param5_ty:ty, + $($param6:ident)? $(_)?: $param6_ty:ty, + $($param7:ident)? $(_)?: $param7_ty:ty, + $($param8:ident)? $(_)?: $param8_ty:ty $(,)? + } => { + Self::$name as extern "C" fn(_, _, _, _, _, _, _, _, _, _) -> _ + }; + { + @cast_extern_fn + @$name:ident + + $($param1:ident)? $(_)?: $param1_ty:ty, + $($param2:ident)? $(_)?: $param2_ty:ty, + $($param3:ident)? $(_)?: $param3_ty:ty, + $($param4:ident)? $(_)?: $param4_ty:ty, + $($param5:ident)? $(_)?: $param5_ty:ty, + $($param6:ident)? $(_)?: $param6_ty:ty, + $($param7:ident)? $(_)?: $param7_ty:ty, + $($param8:ident)? $(_)?: $param8_ty:ty, + $($param9:ident)? $(_)?: $param9_ty:ty $(,)? + } => { + Self::$name as extern "C" fn(_, _, _, _, _, _, _, _, _, _, _) -> _ + }; + { + @cast_extern_fn + @$name:ident + + $($param1:ident)? $(_)?: $param1_ty:ty, + $($param2:ident)? $(_)?: $param2_ty:ty, + $($param3:ident)? $(_)?: $param3_ty:ty, + $($param4:ident)? $(_)?: $param4_ty:ty, + $($param5:ident)? $(_)?: $param5_ty:ty, + $($param6:ident)? $(_)?: $param6_ty:ty, + $($param7:ident)? $(_)?: $param7_ty:ty, + $($param8:ident)? $(_)?: $param8_ty:ty, + $($param9:ident)? $(_)?: $param9_ty:ty, + $($param10:ident)? $(_)?: $param10_ty:ty $(,)? + } => { + Self::$name as extern "C" fn(_, _, _, _, _, _, _, _, _, _, _, _) -> _ + }; + { + @cast_extern_fn + @$name:ident + + $($param1:ident)? $(_)?: $param1_ty:ty, + $($param2:ident)? $(_)?: $param2_ty:ty, + $($param3:ident)? $(_)?: $param3_ty:ty, + $($param4:ident)? $(_)?: $param4_ty:ty, + $($param5:ident)? $(_)?: $param5_ty:ty, + $($param6:ident)? $(_)?: $param6_ty:ty, + $($param7:ident)? $(_)?: $param7_ty:ty, + $($param8:ident)? $(_)?: $param8_ty:ty, + $($param9:ident)? $(_)?: $param9_ty:ty, + $($param10:ident)? $(_)?: $param10_ty:ty, + $($param11:ident)? $(_)?: $param11_ty:ty $(,)? + } => { + Self::$name as extern "C" fn(_, _, _, _, _, _, _, _, _, _, _, _, _) -> _ + }; + { + @cast_extern_fn + @$name:ident + + $($param1:ident)? $(_)?: $param1_ty:ty, + $($param2:ident)? $(_)?: $param2_ty:ty, + $($param3:ident)? $(_)?: $param3_ty:ty, + $($param4:ident)? $(_)?: $param4_ty:ty, + $($param5:ident)? $(_)?: $param5_ty:ty, + $($param6:ident)? $(_)?: $param6_ty:ty, + $($param7:ident)? $(_)?: $param7_ty:ty, + $($param8:ident)? $(_)?: $param8_ty:ty, + $($param9:ident)? $(_)?: $param9_ty:ty, + $($param10:ident)? $(_)?: $param10_ty:ty, + $($param11:ident)? $(_)?: $param11_ty:ty, + $($param12:ident)? $(_)?: $param12_ty:ty $(,)? + } => { + Self::$name as extern "C" fn(_, _, _, _, _, _, _, _, _, _, _, _, _, _) -> _ + }; +} + +/// Declare a new Objective-C class. +/// +/// This is mostly just a convenience macro on top of [`extern_class!`] and +/// the functionality in the [`objc2::declare`] module, but it can really help +/// with cutting down on boilerplate, in particular when defining delegate +/// classes! +/// +/// +/// # Specification +/// +/// This macro consists of three parts; the class definition, the method +/// definition, and the protocol definition. +/// +/// +/// ## Class and ivar definition +/// +/// The class definition works a lot like [`extern_class!`], with the added +/// functionality that you can define custom instance variables on your class, +/// which are then wrapped in a [`objc2::runtime::Ivar`] and made accessible +/// through the class. (E.g. you can use `self.my_ivar` as if it was a normal +/// Rust struct). +/// +/// Note that the class name should be unique across the entire application! +/// As a tip, you can declare the class with the desired unique name like +/// `MyCrateCustomObject` using this macro, and then expose a renamed type +/// alias like `pub type CustomObject = MyCrateCustomObject;` instead. +/// +/// The class is guaranteed to have been created and registered with the +/// Objective-C runtime after the associated function `class` has been called. +/// +/// +/// ## Method definition +/// +/// Within the `impl` block you can define two types of functions; +/// ["associated functions"] and ["methods"]. These are then mapped to the +/// Objective-C equivalents "class methods" and "instance methods". In +/// particular, if you use `self` your method will be registered as an +/// instance method, and if you don't it will be registered as a class method. +/// +/// The desired selector can be specified using a special `@sel(my:selector:)` +/// directive directly before the function definition. +/// +/// A transformation step is performed on the functions (to make them have the +/// correct ABI) and hence they shouldn't really be called manually. (You +/// can't mark them as `pub` for the same reason). Instead, define a new +/// function that calls it via. [`objc2::msg_send!`]. +/// +/// ["associated functions"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods +/// ["methods"]: https://doc.rust-lang.org/reference/items/associated-items.html#methods +/// +/// +/// ## Protocol definition +/// +/// You can specify the protocols that the class should implement, along with +/// any required methods for said protocols. +/// +/// The methods work exactly as normal, they're only put "under" the protocol +/// definition to make things easier to read. +/// +/// +/// # Safety +/// +/// Using this macro requires writing a few `unsafe` markers: +/// +/// `unsafe struct ...` has the following safety requirements: +/// - Same as [`extern_class!`] (the inheritance chain has to be correct). +/// - Any instance variables you specify must either be able to be created +/// using [`MaybeUninit::zeroed`], or be properly initialized in an `init` +/// method. +/// +/// `unsafe impl { ... }` asserts that the types match those that are expected +/// when the method is invoked from Objective-C. Note that there are no +/// safe-guards here; you can easily write `i8`, but if Objective-C thinks +/// it's an `u32`, it will cause UB when called! +/// +/// `unsafe impl protocol ... { ... }` requires that all required methods of +/// the specified protocol is implemented, and that any extra requirements +/// (implicit or explicit) that the protocol has are upheld. The methods in +/// this definition has the same safety requirements as above. +/// +/// [`MaybeUninit::zeroed`]: core::mem::MaybeUninit::zeroed +/// +/// +/// # Examples +/// +/// Declare a class `MyCustomObject` that inherits `NSObject`, has a few +/// instance variables and methods, and implements the `NSCopying` protocol. +/// +/// ``` +/// use std::os::raw::c_int; +/// use objc2::{msg_send, msg_send_bool, msg_send_id}; +/// use objc2::rc::{Id, Owned}; +/// use objc2::runtime::Bool; +/// use objc2_foundation::{declare_class, NSCopying, NSObject, NSZone}; +/// # +/// # #[cfg(feature = "gnustep-1-7")] +/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() }; +/// +/// declare_class! { +/// unsafe struct MyCustomObject: NSObject { +/// foo: u8, +/// pub bar: c_int, +/// } +/// +/// unsafe impl { +/// @sel(initWithFoo:) +/// fn init_with(&mut self, foo: u8) -> Option<&mut Self> { +/// let this: Option<&mut Self> = unsafe { +/// msg_send![super(self, NSObject::class()), init] +/// }; +/// this.map(|this| { +/// // TODO: Initialization through MaybeUninit +/// // (The below is only safe because these variables are +/// // safe to initialize with `MaybeUninit::zeroed`). +/// *this.foo = foo; +/// *this.bar = 42; +/// this +/// }) +/// } +/// +/// @sel(foo) +/// fn __get_foo(&self) -> u8 { +/// *self.foo +/// } +/// +/// @sel(myClassMethod) +/// fn __my_class_method() -> Bool { +/// Bool::YES +/// } +/// } +/// +/// unsafe impl protocol NSCopying { +/// @sel(copyWithZone:) +/// fn copy_with_zone(&self, _zone: *const NSZone) -> *mut Self { +/// let mut obj = Self::new(*self.foo); +/// *obj.bar = *self.bar; +/// obj.autorelease_return() +/// } +/// } +/// } +/// +/// impl MyCustomObject { +/// pub fn new(foo: u8) -> Id { +/// let cls = Self::class(); +/// unsafe { msg_send_id![msg_send_id![cls, alloc], initWithFoo: foo].unwrap() } +/// } +/// +/// pub fn get_foo(&self) -> u8 { +/// unsafe { msg_send![self, foo] } +/// } +/// +/// pub fn my_class_method() -> bool { +/// unsafe { msg_send_bool![Self::class(), myClassMethod] } +/// } +/// } +/// +/// unsafe impl NSCopying for MyCustomObject { +/// type Ownership = Owned; +/// type Output = Self; +/// } +/// +/// fn main() { +/// let obj = MyCustomObject::new(3); +/// assert_eq!(*obj.foo, 3); +/// assert_eq!(*obj.bar, 42); +/// +/// let obj = obj.copy(); +/// assert_eq!(obj.get_foo(), 3); +/// +/// assert!(MyCustomObject::my_class_method()); +/// } +/// ``` +/// +/// Approximately equivalent to the following Objective-C code. +/// +/// ```text +/// #import +/// +/// @interface MyCustomObject: NSObject { +/// // Public ivar +/// int bar; +/// } +/// +/// - (instancetype)initWithFoo:(uint8_t)foo; +/// - (uint8_t)foo; +/// + (BOOL)myClassMethod; +/// +/// @end +/// +/// +/// @implementation MyCustomObject { +/// // Private ivar +/// uint8_t foo; +/// } +/// +/// - (instancetype)initWithFoo:(uint8_t)foo_arg { +/// self = [super init]; +/// if (self) { +/// self->foo = foo_arg; +/// self->bar = 42; +/// } +/// return self; +/// } +/// +/// - (uint8_t)foo { +/// return self->foo; // Or just `foo` +/// } +/// +/// + (BOOL)myClassMethod { +/// return YES; +/// } +/// +/// // NSCopying +/// +/// - (id)copyWithZone:(NSZone *)_zone { +/// MyCustomObject* obj = [[MyCustomObject alloc] initWithFoo: self->foo]; +/// obj->bar = self->bar; +/// return obj; +/// } +/// +/// @end +/// ``` +#[macro_export] +macro_rules! declare_class { + { + $(#[$m:meta])* + unsafe $v:vis struct $name:ident: $inherits:ty $(, $inheritance_rest:ty)* { + $($ivar_v:vis $ivar:ident: $ivar_ty:ty,)* + } + + $( + $(#[$impl_m:meta])* + unsafe impl $(protocol $protocol:ident)? { + $($methods:tt)* + } + )* + } => { + $( + #[allow(non_camel_case_types)] + $ivar_v struct $ivar { + __priv: (), + } + + unsafe impl $crate::objc2::declare::IvarType for $ivar { + type Type = $ivar_ty; + const NAME: &'static str = stringify!($ivar); + } + )* + + $crate::__inner_extern_class! { + @__inner + $(#[$m])* + // SAFETY: Upheld by caller + unsafe $v struct $name<>: $inherits, $($inheritance_rest,)* $crate::objc2::runtime::Object { + // SAFETY: + // - The ivars are in a type used as an Objective-C object. + // - The instance variable is defined in the exact same manner + // in `class` below. + // - Rust prevents having two fields with the same name. + // - Caller upholds that the ivars are properly initialized. + $($ivar_v $ivar: $crate::objc2::declare::Ivar<$ivar>,)* + } + } + + // Creation + impl $name { + #[doc = concat!( + "Get a reference to the Objective-C class `", + stringify!($name), + "`.", + "\n\n", + "May register the class if it wasn't already.", + )] + // TODO: Allow users to configure this? + $v fn class() -> &'static $crate::objc2::runtime::Class { + // TODO: Use `core::cell::LazyCell` + use $crate::__std::sync::Once; + + use $crate::objc2::declare::ClassBuilder; + use $crate::objc2::runtime::Protocol; + static REGISTER_CLASS: Once = Once::new(); + + REGISTER_CLASS.call_once(|| { + let superclass = <$inherits>::class(); + let err_str = concat!( + "could not create new class ", + stringify!($name), + ". Perhaps a class with that name already exists?", + ); + let mut builder = ClassBuilder::new(stringify!($name), superclass).expect(err_str); + + $( + builder.add_ivar::<<$ivar as $crate::objc2::declare::IvarType>::Type>( + <$ivar as $crate::objc2::declare::IvarType>::NAME + ); + )* + + $( + // Implement protocol if any specified + $( + let err_str = concat!("could not find protocol ", stringify!($protocol)); + builder.add_protocol(Protocol::get(stringify!($protocol)).expect(err_str)); + )? + + // Implement methods + // SAFETY: Upheld by caller + unsafe { + $crate::__inner_declare_class! { + @rewrite_methods + @register_out + @builder + + $($methods)* + } + } + )* + + let _cls = builder.register(); + }); + + $crate::objc2::class!($name) + } + } + + // Methods + $( + $(#[$impl_m])* + impl $name { + $crate::__inner_declare_class! { + @rewrite_methods + @method_out + @__builder + + $($methods)* + } + } + )* + }; +} diff --git a/objc2-foundation/src/lib.rs b/objc2-foundation/src/lib.rs index 174fb91fd..c396bc5b7 100644 --- a/objc2-foundation/src/lib.rs +++ b/objc2-foundation/src/lib.rs @@ -65,14 +65,18 @@ pub use self::string::NSString; pub use self::thread::{is_main_thread, is_multi_threaded, MainThreadMarker, NSThread}; pub use self::uuid::NSUUID; pub use self::value::NSValue; +pub use self::zone::NSZone; // Available under Foundation, so makes sense here as well: // https://developer.apple.com/documentation/foundation/numbers_data_and_basic_values?language=objc #[doc(no_inline)] pub use objc2::ffi::{NSInteger, NSUInteger}; +// For macros #[doc(hidden)] pub use core as __core; +#[doc(hidden)] +pub extern crate std as __std; // Expose the version of objc2 that this crate uses pub use objc2; @@ -87,6 +91,8 @@ extern "C" {} #[macro_use] mod macros; +#[macro_use] +mod declare_macro; mod array; mod attributed_string; @@ -106,3 +112,4 @@ mod string; mod thread; mod uuid; mod value; +mod zone; diff --git a/objc2-foundation/src/macros.rs b/objc2-foundation/src/macros.rs index e5fefb014..aa424d015 100644 --- a/objc2-foundation/src/macros.rs +++ b/objc2-foundation/src/macros.rs @@ -13,6 +13,10 @@ /// The traits [`objc2::RefEncode`] and [`objc2::Message`] are implemented to /// allow sending messages to the object and using it in [`objc2::rc::Id`]. /// +/// An associated function `class` is created on the object as a convenient +/// shorthand so that you can do `MyObject::class()` instead of +/// `class!(MyObject)`. +/// /// [`Deref`] and [`DerefMut`] are implemented and delegate to the first /// superclass (direct parent). Auto traits are inherited from this superclass /// as well (this macro effectively just creates a newtype wrapper around the @@ -94,6 +98,19 @@ macro_rules! extern_class { $(#[$m])* unsafe $v struct $name<>: $($inheritance_chain,)+ $crate::objc2::runtime::Object {} } + + impl $name { + #[doc = concat!( + "Get a reference to the Objective-C class `", + stringify!($name), + "`.", + )] + #[inline] + // TODO: Allow users to configure this? + $v fn class() -> &'static $crate::objc2::runtime::Class { + $crate::objc2::class!($name) + } + } }; } @@ -161,12 +178,25 @@ macro_rules! __inner_extern_class { $($p: $pty,)* } } + + impl<$($t $(: $b)?),*> $name<$($t),*> { + #[doc = concat!( + "Get a reference to the Objective-C class `", + stringify!($name), + "`.", + )] + #[inline] + // TODO: Allow users to configure this? + $v fn class() -> &'static $crate::objc2::runtime::Class { + $crate::objc2::class!($name) + } + } }; ( @__inner $(#[$m:meta])* unsafe $v:vis struct $name:ident<$($t:ident $(: $b:ident)?),*>: $inherits:ty $(, $inheritance_rest:ty)* { - $($p:ident: $pty:ty,)* + $($p_v:vis $p:ident: $pty:ty,)* } ) => { $(#[$m])* @@ -174,8 +204,8 @@ macro_rules! __inner_extern_class { #[repr(C)] $v struct $name<$($t $(: $b)?),*> { __inner: $inherits, - // Additional fields (should only be zero-sized PhantomData). - $($p: $pty),* + // Additional fields (should only be zero-sized PhantomData or ivars). + $($p_v $p: $pty),* } unsafe impl<$($t $(: $b)?),*> $crate::objc2::Message for $name<$($t),*> { } @@ -185,12 +215,6 @@ macro_rules! __inner_extern_class { = <$inherits as $crate::objc2::RefEncode>::ENCODING_REF; } - impl<$($t $(: $b)?),*> $name<$($t),*> { - $v fn class() -> &'static $crate::objc2::runtime::Class { - $crate::objc2::class!($name) - } - } - // SAFETY: An instance can always be _used_ in exactly the same way as // it's superclasses (though not necessarily _constructed_ in the same // way, but `Deref` doesn't allow this). diff --git a/objc2-foundation/src/object.rs b/objc2-foundation/src/object.rs index b8dfbe274..2c8484ce7 100644 --- a/objc2-foundation/src/object.rs +++ b/objc2-foundation/src/object.rs @@ -3,7 +3,7 @@ use core::hash; use objc2::rc::{DefaultId, Id, Owned, Shared}; use objc2::runtime::{Class, Object}; -use objc2::{msg_send, msg_send_bool, msg_send_id}; +use objc2::{class, msg_send, msg_send_bool, msg_send_id}; use super::NSString; @@ -12,6 +12,14 @@ __inner_extern_class! { unsafe pub struct NSObject<>: Object {} } +impl NSObject { + /// Get a reference to the Objective-C class `NSObject`. + #[inline] + pub fn class() -> &'static Class { + class!(NSObject) + } +} + impl NSObject { unsafe_def_fn!(pub fn new -> Owned); diff --git a/objc2-foundation/src/zone.rs b/objc2-foundation/src/zone.rs new file mode 100644 index 000000000..465c1a395 --- /dev/null +++ b/objc2-foundation/src/zone.rs @@ -0,0 +1,62 @@ +#[cfg(feature = "gnustep-1-7")] +use objc2::Encode; +use objc2::{Encoding, RefEncode}; + +/// A type used to identify and manage memory zones. +/// +/// Zones are ignored on all newer platforms, you should very rarely need to +/// use this, but may be useful if you need to implement `copyWithZone:` or +/// `allocWithZone:`. +/// +/// See [Apple's documentation](https://developer.apple.com/documentation/foundation/nszone?language=objc). +#[derive(Debug)] +pub struct NSZone { + _inner: [u8; 0], +} + +unsafe impl RefEncode for NSZone { + #[cfg(feature = "apple")] + const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Encoding::Struct("_NSZone", &[])); + #[cfg(feature = "gnustep-1-7")] + const ENCODING_REF: Encoding<'static> = Encoding::Pointer(&Encoding::Struct( + "_NSZone", + &[ + // Functions + Encoding::Pointer(&Encoding::Unknown), + Encoding::Pointer(&Encoding::Unknown), + Encoding::Pointer(&Encoding::Unknown), + Encoding::Pointer(&Encoding::Unknown), + Encoding::Pointer(&Encoding::Unknown), + Encoding::Pointer(&Encoding::Unknown), + // Stats + Encoding::Pointer(&Encoding::Unknown), + // Zone granularity + usize::ENCODING, + // Name of zone + Encoding::Object, + // Next zone + Encoding::Pointer(&Encoding::Struct("_NSZone", &[])), + ], + )); +} + +#[cfg(test)] +mod tests { + use crate::NSObject; + use core::ptr; + use objc2::msg_send_id; + use objc2::rc::{Allocated, Id, Owned}; + + use super::*; + + #[test] + #[cfg_attr( + feature = "gnustep-1-7", + ignore = "The encoding is not really correct yet!" + )] + fn alloc_with_zone() { + let zone: *const NSZone = ptr::null(); + let _obj: Id, Owned> = + unsafe { msg_send_id![NSObject::class(), allocWithZone: zone].unwrap() }; + } +} diff --git a/objc2/CHANGELOG.md b/objc2/CHANGELOG.md index a1508b823..e92519026 100644 --- a/objc2/CHANGELOG.md +++ b/objc2/CHANGELOG.md @@ -39,6 +39,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). * Added `Class::responds_to`. * Added `exception::Exception` object to improve error messages from caught exceptions. +* Added `declare::Ivar` helper struct. This is useful for building safe + abstractions that access instance variables. ### Changed * **BREAKING**: `Sel` is now required to be non-null, which means that you diff --git a/objc2/examples/declare_ivar.rs b/objc2/examples/declare_ivar.rs new file mode 100644 index 000000000..a0028c81d --- /dev/null +++ b/objc2/examples/declare_ivar.rs @@ -0,0 +1,86 @@ +use std::mem::MaybeUninit; + +use objc2::declare::{ClassBuilder, Ivar, IvarType}; +use objc2::rc::{Id, Owned}; +use objc2::runtime::{Bool, Class, Object, Sel}; +use objc2::{class, msg_send, msg_send_id, sel, Encoding, Message, RefEncode}; + +// Helper types for the two instance variables + +struct CustomIvar1; +unsafe impl IvarType for CustomIvar1 { + type Type = i32; + const NAME: &'static str = "ivar1"; +} + +struct CustomIvar2; +unsafe impl IvarType for CustomIvar2 { + type Type = Bool; + const NAME: &'static str = "ivar2"; +} + +/// Struct that represents the desired object +#[repr(C)] +pub struct CustomObject { + inner: Object, + // SAFETY: The instance variables are used within an object, and they are + // correctly declared in `create_class`. + ivar1: Ivar, + ivar2: Ivar, +} + +// SAFETY: `Ivar` is zero-sized, so it can be ignored +unsafe impl RefEncode for CustomObject { + const ENCODING_REF: Encoding<'static> = Encoding::Object; +} +unsafe impl Message for CustomObject {} + +pub fn create_class() -> &'static Class { + let superclass = class!(NSObject); + let mut builder = ClassBuilder::new("CustomObject", superclass).unwrap(); + + builder.add_ivar::<::Type>(CustomIvar1::NAME); + builder.add_ivar::<::Type>(CustomIvar2::NAME); + + #[repr(C)] + pub struct PartialInit { + inner: Object, + ivar1: Ivar>, + ivar2: Ivar>, + } + unsafe impl RefEncode for PartialInit { + const ENCODING_REF: Encoding<'static> = Encoding::Object; + } + unsafe impl Message for PartialInit {} + + impl PartialInit { + extern "C" fn init(&mut self, _cmd: Sel) -> Option<&mut Self> { + let this: Option<&mut Self> = unsafe { msg_send![super(self, class!(NSObject)), init] }; + this.map(|this| { + this.ivar1.write(42); + this.ivar2.write(Bool::from(true)); + this + }) + } + } + + unsafe { + builder.add_method(sel!(init), PartialInit::init as extern "C" fn(_, _) -> _); + } + + builder.register() +} + +fn main() { + let cls = create_class(); + let mut obj: Id = unsafe { msg_send_id![cls, new].unwrap() }; + + println!("Ivar1: {:?}", obj.ivar1); + println!("Ivar2: {:?}", obj.ivar2); + + *obj.ivar1 += 1; + *obj.ivar2 = Bool::from(false); + + println!("Ivar1: {:?}", obj.ivar1); + println!("Ivar2: {:?}", obj.ivar2); +} diff --git a/objc2/src/declare.rs b/objc2/src/declare.rs index 51b096044..f189abe9a 100644 --- a/objc2/src/declare.rs +++ b/objc2/src/declare.rs @@ -37,6 +37,9 @@ //! decl.register(); //! ``` +mod ivar; +mod ivar_forwarding_impls; + use alloc::format; use alloc::string::ToString; use core::mem; @@ -48,6 +51,8 @@ use std::ffi::CString; use crate::runtime::{Bool, Class, Imp, Object, Protocol, Sel}; use crate::{ffi, Encode, EncodeArguments, Encoding, Message, RefEncode}; +pub use ivar::{Ivar, IvarType}; + pub(crate) mod private { pub trait Sealed {} } @@ -260,7 +265,8 @@ impl ClassBuilder { assert_eq!( sel_args, encs.len(), - "Selector accepts {} arguments, but function accepts {}", + "Selector {:?} accepts {} arguments, but function accepts {}", + sel, sel_args, encs.len(), ); @@ -301,7 +307,8 @@ impl ClassBuilder { assert_eq!( sel_args, encs.len(), - "Selector accepts {} arguments, but function accepts {}", + "Selector {:?} accepts {} arguments, but function accepts {}", + sel, sel_args, encs.len(), ); @@ -412,7 +419,8 @@ impl ProtocolBuilder { assert_eq!( sel_args, encs.len(), - "Selector accepts {} arguments, but function accepts {}", + "Selector {:?} accepts {} arguments, but function accepts {}", + sel, sel_args, encs.len(), ); diff --git a/objc2/src/declare/ivar.rs b/objc2/src/declare/ivar.rs new file mode 100644 index 000000000..78e12401d --- /dev/null +++ b/objc2/src/declare/ivar.rs @@ -0,0 +1,241 @@ +use core::fmt; +use core::marker::PhantomData; +use core::mem::MaybeUninit; +use core::ops::{Deref, DerefMut}; +use core::ptr::NonNull; + +use crate::runtime::Object; +use crate::Encode; + +/// Helper trait for defining instance variables. +/// +/// This should be implemented for an empty marker type, which can then be +/// used within [`Ivar`] to refer to the instance variable. +/// +/// +/// # Safety +/// +/// Really, [`Ivar`] should be marked as `unsafe`, but since we can't do that +/// we'll mark this trait as `unsafe` instead. See [`Ivar`] for safety +/// requirements. +/// +/// +/// # Examples +/// +/// Create an instance variable `myCustomIvar` with type `i32`. +/// +/// ``` +/// use objc2::declare::IvarType; +/// +/// // Helper type +/// struct MyCustomIvar; +/// +/// unsafe impl IvarType for MyCustomIvar { +/// type Type = i32; +/// const NAME: &'static str = "myCustomIvar"; +/// } +/// +/// // `Ivar` can now be used +/// ``` +pub unsafe trait IvarType { + /// The type of the instance variable. + type Type: Encode; + /// The name of the instance variable. + const NAME: &'static str; +} + +unsafe impl IvarType for MaybeUninit { + type Type = MaybeUninit; + const NAME: &'static str = T::NAME; +} + +/// A wrapper type over a custom instance variable. +/// +/// This type is not meant to be constructed by itself, it must reside within +/// another struct meant to represent an Objective-C object. +/// +/// On [`Deref`] it then uses the [`IvarType::NAME`] string to access the ivar +/// of the containing object. +/// +/// Note that this is not ([currently][zst-hack]) allowed by [stacked +/// borrows][sb], but due to [`Object`] being a zero-sized type such that we +/// don't have provenance over the ivars anyhow, this should be just as sound +/// as normal instance variable access. +/// +/// [sb]: https://github.com/rust-lang/unsafe-code-guidelines/blob/e21202c60c7be03dd2ab016ada92fb5305d40438/wip/stacked-borrows.md +/// [zst-hack]: https://github.com/rust-lang/unsafe-code-guidelines/issues/305 +/// +/// +/// # Safety +/// +/// This must be used within a type that act as an Objective-C object. In +/// particular, this is never safe to have on the stack by itself. +/// +/// Additionally, the instance variable described by `T` must be available on +/// the specific instance, and be of the exact same type. +/// +/// Finally, two ivars with the same name must not be used on the same object. +/// +/// +/// # Examples +/// +/// ``` +/// use objc2::declare::{Ivar, IvarType}; +/// use objc2::runtime::Object; +/// # +/// # #[cfg(feature = "gnustep-1-7")] +/// # unsafe { objc2::__gnustep_hack::get_class_to_force_linkage() }; +/// +/// // Declare ivar with given type and name +/// struct MyCustomIvar; +/// unsafe impl IvarType for MyCustomIvar { +/// type Type = i32; +/// const NAME: &'static str = "myCustomIvar"; +/// } +/// +/// // Custom object +/// #[repr(C)] +/// pub struct MyObject { +/// inner: Object, +/// // SAFETY: The instance variable is used within an object, and it is +/// // properly declared below. +/// my_ivar: Ivar, +/// } +/// +/// # use objc2::class; +/// # use objc2::declare::ClassBuilder; +/// # let mut builder = ClassBuilder::new("MyObject", class!(NSObject)).unwrap(); +/// // Declare the class and add the instance variable to it +/// builder.add_ivar::<::Type>(MyCustomIvar::NAME); +/// # let _cls = builder.register(); +/// +/// let obj: MyObject; +/// // You can now access `obj.my_ivar` +/// ``` +/// +/// See also the `declare_ivar.rs` example. +#[repr(C)] +// Must not be `Copy` nor `Clone`! +pub struct Ivar { + /// Make this type allowed in `repr(C)` + inner: [u8; 0], + /// For proper variance and auto traits + item: PhantomData, +} + +impl Deref for Ivar { + type Target = T::Type; + + #[inline] + fn deref(&self) -> &Self::Target { + // SAFETY: The user ensures that this is placed in a struct that can + // be reinterpreted as an `Object`. Since `Ivar` can never be + // constructed by itself (and is neither Copy nor Clone), we know that + // it is guaranteed to _stay_ in said struct. + // + // Even if the user were to do `mem::swap`, the `Ivar` has a unique + // type (and does not hold any data), so that wouldn't break anything. + // + // Note: We technically don't have provenance over the object, nor the + // ivar, but the object doesn't have provenance over the ivar either, + // so that is fine. + let ptr = NonNull::from(self).cast::(); + let obj = unsafe { ptr.as_ref() }; + + // SAFETY: User ensures that the `Ivar` is only used when the ivar + // exists and has the correct type + unsafe { obj.ivar::(T::NAME) } + } +} + +impl DerefMut for Ivar { + #[inline] + fn deref_mut(&mut self) -> &mut Self::Target { + let ptr = NonNull::from(self).cast::(); + // SAFETY: Same as `deref`. + // + // Note: We don't use `mut` because the user might have two mutable + // references to different ivars, as such: + // + // ``` + // #[repr(C)] + // struct X { + // inner: Object, + // ivar1: Ivar, + // ivar2: Ivar, + // } + // + // let mut x: X; + // let ivar1: &mut Ivar = &mut x.ivar1; + // let ivar2: &mut Ivar = &mut x.ivar2; + // ``` + // + // And using `mut` would create aliasing mutable reference to the + // object. + // + // TODO: Not entirely sure, it might be safe to just do `as_mut`, but + // this is definitely safe. + let obj = unsafe { ptr.as_ref() }; + + // SAFETY: Same as `deref` + // + // Safe as mutable because there is only one access to a particular + // ivar at a time (since we have `&mut self`). `Object` is + // `UnsafeCell`, so mutable access through `&Object` is allowed. + unsafe { obj.ivar_ptr::(T::NAME).as_mut().unwrap_unchecked() } + } +} + +/// Format as a pointer to the instance variable. +impl fmt::Pointer for Ivar { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let ptr: *const T::Type = &**self; + fmt::Pointer::fmt(&ptr, f) + } +} + +#[cfg(test)] +mod tests { + use core::mem; + use core::panic::{RefUnwindSafe, UnwindSafe}; + + use super::*; + use crate::{test_utils, MessageReceiver}; + + struct TestIvar; + + unsafe impl IvarType for TestIvar { + type Type = u32; + const NAME: &'static str = "_foo"; + } + + #[repr(C)] + struct IvarTestObject { + inner: Object, + foo: Ivar, + } + + #[test] + fn auto_traits() { + fn assert_auto_traits() {} + assert_auto_traits::>(); + + // Ensure that `Ivar` is zero-sized + assert_eq!(mem::size_of::>(), 0); + assert_eq!(mem::align_of::>(), 1); + } + + #[test] + fn access_ivar() { + let mut obj = test_utils::custom_object(); + let _: () = unsafe { msg_send![&mut obj, setFoo: 42u32] }; + + let obj = unsafe { + obj.__as_raw_receiver() + .cast::() + .as_ref() + .unwrap() + }; + assert_eq!(*obj.foo, 42); + } +} diff --git a/objc2/src/declare/ivar_forwarding_impls.rs b/objc2/src/declare/ivar_forwarding_impls.rs new file mode 100644 index 000000000..dd4dc0d7f --- /dev/null +++ b/objc2/src/declare/ivar_forwarding_impls.rs @@ -0,0 +1,337 @@ +//! Trivial forwarding impls on `Ivar`. +//! +//! Kept here to keep `ivar.rs` free from this boilerplate. +//! +//! `#[inline]` is used where the standard library `Box` uses it. + +#![forbid(unsafe_code)] + +// use alloc::borrow; +use alloc::string::String; +use alloc::vec::Vec; +use core::cmp::Ordering; +use core::fmt; +use core::future::Future; +use core::hash; +use core::iter::FusedIterator; +use core::ops::{Deref, DerefMut}; +use core::pin::Pin; +use core::task::{Context, Poll}; +use std::error::Error; +use std::io; + +use super::{Ivar, IvarType}; + +impl PartialEq for Ivar +where + T::Type: PartialEq, +{ + #[inline] + fn eq(&self, other: &Self) -> bool { + (**self).eq(&**other) + } + + #[inline] + #[allow(clippy::partialeq_ne_impl)] + fn ne(&self, other: &Self) -> bool { + (**self).ne(&**other) + } +} + +impl Eq for Ivar where T::Type: Eq {} + +impl PartialOrd for Ivar +where + T::Type: PartialOrd, +{ + #[inline] + fn partial_cmp(&self, other: &Self) -> Option { + (**self).partial_cmp(&**other) + } + #[inline] + fn lt(&self, other: &Self) -> bool { + (**self).lt(&**other) + } + #[inline] + fn le(&self, other: &Self) -> bool { + (**self).le(&**other) + } + #[inline] + fn ge(&self, other: &Self) -> bool { + (**self).ge(&**other) + } + #[inline] + fn gt(&self, other: &Self) -> bool { + (**self).gt(&**other) + } +} + +impl Ord for Ivar +where + T::Type: Ord, +{ + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + (**self).cmp(&**other) + } +} + +impl hash::Hash for Ivar +where + T::Type: hash::Hash, +{ + fn hash(&self, state: &mut H) { + (**self).hash(state) + } +} + +impl hash::Hasher for Ivar +where + T::Type: hash::Hasher, +{ + fn finish(&self) -> u64 { + (**self).finish() + } + fn write(&mut self, bytes: &[u8]) { + (**self).write(bytes) + } + fn write_u8(&mut self, i: u8) { + (**self).write_u8(i) + } + fn write_u16(&mut self, i: u16) { + (**self).write_u16(i) + } + fn write_u32(&mut self, i: u32) { + (**self).write_u32(i) + } + fn write_u64(&mut self, i: u64) { + (**self).write_u64(i) + } + fn write_u128(&mut self, i: u128) { + (**self).write_u128(i) + } + fn write_usize(&mut self, i: usize) { + (**self).write_usize(i) + } + fn write_i8(&mut self, i: i8) { + (**self).write_i8(i) + } + fn write_i16(&mut self, i: i16) { + (**self).write_i16(i) + } + fn write_i32(&mut self, i: i32) { + (**self).write_i32(i) + } + fn write_i64(&mut self, i: i64) { + (**self).write_i64(i) + } + fn write_i128(&mut self, i: i128) { + (**self).write_i128(i) + } + fn write_isize(&mut self, i: isize) { + (**self).write_isize(i) + } +} + +impl fmt::Display for Ivar +where + T::Type: fmt::Display, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl fmt::Debug for Ivar +where + T::Type: fmt::Debug, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + (**self).fmt(f) + } +} + +impl Iterator for Ivar +where + I::Type: Iterator, +{ + type Item = ::Item; + fn next(&mut self) -> Option<::Item> { + (**self).next() + } + fn size_hint(&self) -> (usize, Option) { + (**self).size_hint() + } + fn nth(&mut self, n: usize) -> Option<::Item> { + (**self).nth(n) + } +} + +impl DoubleEndedIterator for Ivar +where + I::Type: DoubleEndedIterator, +{ + fn next_back(&mut self) -> Option<::Item> { + (**self).next_back() + } + fn nth_back(&mut self, n: usize) -> Option<::Item> { + (**self).nth_back(n) + } +} + +impl ExactSizeIterator for Ivar +where + I::Type: ExactSizeIterator, +{ + fn len(&self) -> usize { + (**self).len() + } +} + +impl FusedIterator for Ivar where I::Type: FusedIterator {} + +// impl borrow::Borrow for Ivar { +// fn borrow(&self) -> &T::Type { +// Deref::deref(self) +// } +// } +// +// impl borrow::BorrowMut for Ivar { +// fn borrow_mut(&mut self) -> &mut T::Type { +// DerefMut::deref_mut(self) +// } +// } + +impl AsRef for Ivar { + fn as_ref(&self) -> &T::Type { + Deref::deref(self) + } +} + +impl AsMut for Ivar { + fn as_mut(&mut self) -> &mut T::Type { + DerefMut::deref_mut(self) + } +} + +impl Error for Ivar +where + T::Type: Error, +{ + fn source(&self) -> Option<&(dyn Error + 'static)> { + (**self).source() + } +} + +impl io::Read for Ivar +where + T::Type: io::Read, +{ + #[inline] + fn read(&mut self, buf: &mut [u8]) -> io::Result { + (**self).read(buf) + } + + #[inline] + fn read_vectored(&mut self, bufs: &mut [io::IoSliceMut<'_>]) -> io::Result { + (**self).read_vectored(bufs) + } + + #[inline] + fn read_to_end(&mut self, buf: &mut Vec) -> io::Result { + (**self).read_to_end(buf) + } + + #[inline] + fn read_to_string(&mut self, buf: &mut String) -> io::Result { + (**self).read_to_string(buf) + } + + #[inline] + fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> { + (**self).read_exact(buf) + } +} + +impl io::Write for Ivar +where + T::Type: io::Write, +{ + #[inline] + fn write(&mut self, buf: &[u8]) -> io::Result { + (**self).write(buf) + } + + #[inline] + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + (**self).write_vectored(bufs) + } + + #[inline] + fn flush(&mut self) -> io::Result<()> { + (**self).flush() + } + + #[inline] + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + (**self).write_all(buf) + } + + #[inline] + fn write_fmt(&mut self, fmt: fmt::Arguments<'_>) -> io::Result<()> { + (**self).write_fmt(fmt) + } +} + +impl io::Seek for Ivar +where + T::Type: io::Seek, +{ + #[inline] + fn seek(&mut self, pos: io::SeekFrom) -> io::Result { + (**self).seek(pos) + } + + #[inline] + fn stream_position(&mut self) -> io::Result { + (**self).stream_position() + } +} + +impl io::BufRead for Ivar +where + T::Type: io::BufRead, +{ + #[inline] + fn fill_buf(&mut self) -> io::Result<&[u8]> { + (**self).fill_buf() + } + + #[inline] + fn consume(&mut self, amt: usize) { + (**self).consume(amt) + } + + #[inline] + fn read_until(&mut self, byte: u8, buf: &mut Vec) -> io::Result { + (**self).read_until(byte, buf) + } + + #[inline] + fn read_line(&mut self, buf: &mut String) -> io::Result { + (**self).read_line(buf) + } +} + +impl Future for Ivar +where + T::Type: Future + Unpin, +{ + type Output = ::Output; + + fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { + ::poll(Pin::new(&mut *self), cx) + } +} + +// TODO: impl Fn traits, CoerceUnsized, Stream and so on when stabilized