Macro objc2::extern_class
source · macro_rules! extern_class { ( $(#[$m:meta])* $v:vis struct $name:ident; $(#[$impl_m:meta])* unsafe impl ClassType for $for:ty { $(#[inherits($($inheritance_rest:ty),+)])? type Super = $superclass:ty; type Mutability = $mutability:ty; $(const NAME: &'static str = $name_const:expr;)? } ) => { ... }; ( $(#[$m:meta])* $v:vis struct $name:ident { $($field_vis:vis $field:ident: $field_ty:ty,)* } $(#[$impl_m:meta])* unsafe impl ClassType for $for:ty { $(#[inherits($($inheritance_rest:ty),+)])? type Super = $superclass:ty; type Mutability = $mutability:ty; $(const NAME: &'static str = $name_const:expr;)? } ) => { ... }; }
Expand description
Create a new type to represent a class.
This is similar to an @interface
declaration in Objective-C.
It is useful for things like objc2-foundation
, which needs to create
interfaces to existing, externally defined classes like NSString
,
NSURL
and so on, but can also be useful for users that have custom
classes written in Objective-C that they want to access from Rust.
§Specification
The syntax is similar enough to Rust syntax that if you invoke the macro
with parentheses (as opposed to curly brackets), rustfmt
will be able to
format the contents (so e.g. as extern_class!( ... );
).
The macro creates an opaque struct containing the superclass (which means that auto traits are inherited from the superclass), and implements the following traits for it to allow easier usage as an Objective-C object:
RefEncode
Message
ClassType
Deref<Target = $superclass>
DerefMut
AsRef<$inheritance_chain>
AsMut<$inheritance_chain>
Borrow<$inheritance_chain>
BorrowMut<$inheritance_chain>
The macro allows specifying zero-sized fields like PhantomData
on the
struct.
You can add most attributes to the class, including #[cfg(...)]
,
#[derive(...)]
and doc comments (but not ABI-modifying attributes like
#[repr(...)]
).
§ClassType
implementation
The syntax of this macro neatly documents that it implements the
ClassType
trait for you, though to do so you need to provide it the
following:
-
The
Super
class.Due to Rust trait limitations, specifying e.g. the superclass
NSData
would not give you the ability to convert via.AsRef
toNSObject
. Therefore, you may optionally specify additional parts of the inheritance chain using an#[inherits(...)]
attribute. -
The class’
Mutability
. -
Optionally, the class’
NAME
- if not specified, this will default to the struct name.
You may add #[cfg(...)]
attributes to the ClassType
impl, and then it
will work as expected. No other attributes are supported.
§Safety
This macro implements the three unsafe traits RefEncode
, Message
and ClassType
for you, and while it can ensure most of the required
properties in those, it cannot ensure all of them.
In particular, when writing unsafe
on impl ClassType
, you must ensure
that:
-
ClassType::Super
is correct. -
ClassType::Mutability
is correct.See
ClassType
’s safety section for further details on what this entails.
§Examples
Create a new type to represent the NSFormatter
class (for demonstration,
objc2_foundation::NSFormatter
exist for exactly this purpose).
use objc2_foundation::{NSCoding, NSCopying, NSObjectProtocol};
use objc2::rc::Retained;
use objc2::runtime::NSObject;
use objc2::{extern_class, msg_send_id, mutability, ClassType};
extern_class!(
/// An example description.
#[derive(PartialEq, Eq, Hash)] // Uses the superclass' implementation
// Specify the class and struct name to be used
pub struct NSFormatter;
// Specify the superclass, in this case `NSObject`
unsafe impl ClassType for NSFormatter {
type Super = NSObject;
type Mutability = mutability::InteriorMutable;
// Optionally, specify the name of the class, if it differs from
// the struct name.
// const NAME: &'static str = "NSFormatter";
}
);
// Note: We have to specify the protocols for the superclasses as well,
// since Rust doesn't do inheritance.
unsafe impl NSObjectProtocol for NSFormatter {}
unsafe impl NSCopying for NSFormatter {}
unsafe impl NSCoding for NSFormatter {}
fn main() {
// Provided by the implementation of `ClassType`
let cls = NSFormatter::class();
// `NSFormatter` implements `Message`:
let obj: Retained<NSFormatter> = unsafe { msg_send_id![cls, new] };
}
Represent the NSDateFormatter
class, using the NSFormatter
type we
declared previously to specify as its superclass.
use objc2_foundation::{NSCoding, NSCopying, NSObjectProtocol};
use objc2::runtime::NSObject;
use objc2::{extern_class, mutability, ClassType};
extern_class!(
#[derive(PartialEq, Eq, Hash)]
pub struct NSDateFormatter;
unsafe impl ClassType for NSDateFormatter {
// Specify the correct inheritance chain
#[inherits(NSObject)]
type Super = NSFormatter;
type Mutability = mutability::InteriorMutable;
}
);
// Similarly, we can specify the protocols that this implements here:
unsafe impl NSObjectProtocol for NSFormatter {}
unsafe impl NSCopying for NSDateFormatter {}
unsafe impl NSCoding for NSDateFormatter {}
See the source code of objc2-foundation
for many more examples.