Skip to content

Commit 44ab9df

Browse files
committed
Include examples in objc2 documentation
1 parent 3dcdc55 commit 44ab9df

File tree

9 files changed

+159
-82
lines changed

9 files changed

+159
-82
lines changed

crates/objc2/examples/class_with_lifetime.rs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
//! A custom Objective-C class with a lifetime parameter.
2-
//!
3-
//! Note that we can't use the `declare_class!` macro for this, it doesn't
4-
//! support such use-cases. Instead, we'll declare the class manually!
1+
//! Note: We can't use the `declare_class!` macro for this, it doesn't support
2+
//! such use-cases (yet). Instead, we'll declare the class manually.
53
#![deny(unsafe_op_in_unsafe_fn)]
64
use std::marker::PhantomData;
75
use std::sync::Once;
@@ -45,11 +43,11 @@ unsafe impl RefEncode for MyObject<'_> {
4543
unsafe impl Message for MyObject<'_> {}
4644

4745
impl<'a> MyObject<'a> {
48-
unsafe extern "C" fn init_with_ptr(
49-
&mut self,
46+
unsafe extern "C" fn init_with_ptr<'s>(
47+
&'s mut self,
5048
_cmd: Sel,
5149
ptr: Option<&'a mut u8>,
52-
) -> Option<&'a mut Self> {
50+
) -> Option<&'s mut Self> {
5351
let this: Option<&mut Self> = unsafe { msg_send![super(self), init] };
5452
this.map(|this| {
5553
// Properly initialize the number reference
@@ -112,23 +110,30 @@ unsafe impl<'a> ClassType for MyObject<'a> {
112110

113111
fn main() {
114112
let mut number = 54;
113+
115114
let mut obj = MyObject::new(&mut number);
115+
assert_eq!(*obj.get(), 54);
116116

117-
// It is not possible to convert to `Id<NSObject>` since that would loose
117+
// It is not possible to convert to `Id<NSObject>`, since that would loose
118118
// the lifetime information that `MyObject` stores.
119+
//
119120
// let obj = Id::into_super(obj);
120121
//
121-
// Neither is it possible to clone the object, since it is marked as
122-
// `Mutable` in `ClassType::Mutability`.
122+
// Neither is it possible to clone or retain the object, since it is
123+
// marked as `Mutable` in `ClassType::Mutability`.
124+
//
123125
// let obj2 = obj.clone();
126+
//
127+
// Finally, it is not possible to access `number` any more, since `obj`
128+
// holds a mutable reference to it.
129+
//
130+
// assert_eq!(number, 7);
124131

125-
println!("Number: {}", obj.get());
126-
132+
// But we can now mutate the referenced `number`
127133
obj.set(7);
128-
// Won't compile, since `obj` holds a mutable reference to number
129-
// println!("Number: {}", number);
130-
println!("Number: {}", obj.get());
134+
assert_eq!(*obj.get(), 7);
131135

132136
drop(obj);
133-
println!("Number: {number}");
137+
// And now that we've dropped `obj`, we can access `number` again
138+
assert_eq!(number, 7);
134139
}

crates/objc2/examples/encode_core_graphics.rs

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ use objc2::encode::{Encode, Encoding};
22

33
#[cfg(target_pointer_width = "32")]
44
type CGFloat = f32;
5-
65
#[cfg(target_pointer_width = "64")]
76
type CGFloat = f64;
87

@@ -12,6 +11,7 @@ struct CGPoint {
1211
y: CGFloat,
1312
}
1413

14+
// SAFETY: The struct is `repr(C)`, and the encoding is correct.
1515
unsafe impl Encode for CGPoint {
1616
const ENCODING: Encoding = Encoding::Struct("CGPoint", &[CGFloat::ENCODING, CGFloat::ENCODING]);
1717
}
@@ -22,6 +22,7 @@ struct CGSize {
2222
height: CGFloat,
2323
}
2424

25+
// SAFETY: The struct is `repr(C)`, and the encoding is correct.
2526
unsafe impl Encode for CGSize {
2627
const ENCODING: Encoding = Encoding::Struct("CGSize", &[CGFloat::ENCODING, CGFloat::ENCODING]);
2728
}
@@ -32,10 +33,17 @@ struct CGRect {
3233
size: CGSize,
3334
}
3435

36+
// SAFETY: The struct is `repr(C)`, and the encoding is correct.
3537
unsafe impl Encode for CGRect {
3638
const ENCODING: Encoding = Encoding::Struct("CGRect", &[CGPoint::ENCODING, CGSize::ENCODING]);
3739
}
3840

3941
fn main() {
40-
println!("{}", CGRect::ENCODING);
42+
let expected = if cfg!(target_pointer_width = "64") {
43+
"{CGRect={CGPoint=dd}{CGSize=dd}}"
44+
} else {
45+
"{CGRect={CGPoint=ff}{CGSize=ff}}"
46+
};
47+
48+
assert!(CGRect::ENCODING.equivalent_to_str(expected));
4149
}

crates/objc2/examples/encode_ns_string.rs

Lines changed: 0 additions & 25 deletions
This file was deleted.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use objc2::encode::{Encode, Encoding, RefEncode};
2+
use objc2::runtime::Object;
3+
4+
#[repr(transparent)]
5+
struct NSString {
6+
// `NSString` has the same layout / works the same as `Object`.
7+
_priv: Object,
8+
}
9+
10+
// We don't know the size of NSString, so we can only hold pointers to it.
11+
//
12+
// SAFETY: The string is `repr(transparent)` over `Object`.
13+
unsafe impl RefEncode for NSString {
14+
const ENCODING_REF: Encoding = Encoding::Object;
15+
}
16+
17+
fn main() {
18+
// The `RefEncode` implementation provide an `Encode` implementation for
19+
// pointers to the object.
20+
assert_eq!(<*const NSString>::ENCODING, Encoding::Object);
21+
assert_eq!(<*mut NSString>::ENCODING, Encoding::Object);
22+
assert_eq!(<&NSString>::ENCODING, Encoding::Object);
23+
assert_eq!(<&mut NSString>::ENCODING, Encoding::Object);
24+
assert_eq!(<Option<&NSString>>::ENCODING, Encoding::Object);
25+
assert_eq!(<Option<&mut NSString>>::ENCODING, Encoding::Object);
26+
}

crates/objc2/examples/encode_ns_uinteger.rs renamed to crates/objc2/examples/encode_nsuinteger.rs

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
1-
//! Implementing `Encode` and `RefEncode` for `NSUInteger`.
2-
//!
3-
//! Note that in this case `NSUInteger` could actually just be a type alias
4-
//! for `usize`, and that's already available under `objc2::ffi::NSUInteger`.
51
use objc2::encode::{Encode, Encoding, RefEncode};
62

3+
// Note: In this case `NSUInteger` could actually just be a type alias for
4+
// `usize`, and actually that's already available as `objc2::ffi::NSUInteger`.
75
#[repr(transparent)]
86
struct NSUInteger {
97
_inner: usize,
108
}
119

1210
// SAFETY: `NSUInteger` has the same `repr` as `usize`.
1311
unsafe impl Encode for NSUInteger {
14-
/// Running `@encode(NSUInteger)` gives `Q` on 64-bit systems and `I` on
15-
/// 32-bit systems. This corresponds exactly to `usize`, which is also how
16-
/// we've defined our struct.
12+
// Running `@encode(NSUInteger)` gives `Q` on 64-bit systems and `I` on
13+
// 32-bit systems. This corresponds exactly to `usize`, which is also how
14+
// we've defined our struct.
1715
const ENCODING: Encoding = usize::ENCODING;
1816
}
1917

2018
// SAFETY: `&NSUInteger` has the same representation as `&usize`.
2119
unsafe impl RefEncode for NSUInteger {
22-
/// Running `@encode(NSUInteger*)` gives `^Q` on 64-bit systems and `^I`
23-
/// on 32-bit systems. So implementing `RefEncode` as a plain pointer is
24-
/// correct.
20+
// Running `@encode(NSUInteger*)` gives `^Q` on 64-bit systems and `^I` on
21+
// 32-bit systems. So implementing `RefEncode` as a plain pointer is
22+
// correct.
2523
const ENCODING_REF: Encoding = Encoding::Pointer(&NSUInteger::ENCODING);
2624
}
2725

crates/objc2/examples/encode_opaque_type.rs

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
1-
//! Implementing `RefEncode` for `NSDecimal`.
21
use objc2::encode::{Encoding, RefEncode};
32

4-
/// We choose in this case to represent `NSDecimal` as an opaque struct
5-
/// (and in the future as an `extern type`) because we don't know much
6-
/// about the internals.
7-
///
8-
/// Therefore we do not implement `Encode`, but when implementing `RefEncode`
9-
/// the type-encoding still has to be correct.
3+
// We choose in this case to represent `NSDecimal` as an opaque struct because
4+
// we don't know much about the internals.
5+
//
6+
// Therefore we do not implement `Encode`, but when implementing `RefEncode`,
7+
// the type-encoding still has to be correct.
108
#[repr(C)]
119
struct NSDecimal {
10+
// Note: This should be an [extern type][rfc-1861] instead, when that
11+
// becomes possible, for now we use this as a workaround.
12+
//
13+
// [rfc-1861]: https://rust-lang.github.io/rfcs/1861-extern-types.html
1214
_priv: [u8; 0],
1315
}
1416

15-
// SAFETY: `&NSDecimal` is a pointer.
17+
// SAFETY: `&NSDecimal` is a valid pointer, and the encoding is correct.
1618
unsafe impl RefEncode for NSDecimal {
17-
// Running `@encode` on `NSDecimal*` on my 64-bit system gives `^{?=cCCC[38C]}`.
19+
// Running `@encode` on `NSDecimal*` on my 64-bit system gives
20+
// `^{?=cCCC[38C]}`. On other systems it may be something else!
1821
const ENCODING_REF: Encoding = Encoding::Pointer(&Encoding::Struct(
1922
"?",
2023
&[

crates/objc2/src/class_type.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,31 +43,68 @@ use crate::Message;
4343
/// Use the trait to access the [`Class`] of an object.
4444
///
4545
/// ```
46-
/// use objc2::ClassType;
47-
/// use objc2::runtime::NSObject;
46+
/// use objc2::{ClassType, msg_send_id};
47+
/// use objc2::rc::Id;
48+
/// # use objc2::runtime::{NSObject as MyObject};
49+
///
50+
/// // Get the class of the object.
51+
/// let cls = <MyObject as ClassType>::class();
52+
/// // Or, since the trait is in scope, just:
53+
/// let cls = MyObject::class();
54+
///
55+
/// // We can now access properties of the class.
56+
/// assert_eq!(cls.name(), MyObject::NAME);
57+
///
58+
/// // And we can send messages to the class.
59+
/// //
60+
/// // SAFETY:
61+
/// // - The class is `MyObject`, which can safely be initialized with `new`.
62+
/// // - The return type is correctly specified.
63+
/// let obj: Id<MyObject> = unsafe { msg_send_id![cls, new] };
64+
/// ```
65+
///
66+
/// Use the trait to allocate a new instance of an object.
67+
///
68+
/// ```
69+
/// use objc2::{ClassType, msg_send_id};
70+
/// use objc2::rc::Id;
71+
/// # use objc2::runtime::{NSObject as MyObject};
72+
///
73+
/// let obj = MyObject::alloc();
4874
///
49-
/// // Get the class of `NSObject`
50-
/// let cls = <NSObject as ClassType>::class(); // Or just `NSObject::class()`
51-
/// assert_eq!(cls.name(), NSObject::NAME);
75+
/// // Now we can call initializers on this newly allocated object.
76+
/// //
77+
/// // SAFETY: `MyObject` can safely be initialized with `init`.
78+
/// let obj: Id<MyObject> = unsafe { msg_send_id![obj, init] };
5279
/// ```
5380
///
54-
/// Use the [`extern_class!`][crate::extern_class] macro to implement this
55-
/// trait for a type.
81+
/// Use the [`extern_class!`][crate::extern_class] macro to easily implement
82+
/// this trait for a type.
5683
///
57-
/// ```no_run
84+
/// ```
5885
/// use objc2::runtime::NSObject;
5986
/// use objc2::{extern_class, mutability, ClassType};
6087
///
6188
/// extern_class!(
6289
/// struct MyClass;
6390
///
91+
/// // SAFETY: The superclass and the mutability is correctly specified.
6492
/// unsafe impl ClassType for MyClass {
6593
/// type Super = NSObject;
6694
/// type Mutability = mutability::InteriorMutable;
95+
/// # // For testing purposes
96+
/// # const NAME: &'static str = "NSObject";
6797
/// }
6898
/// );
6999
///
70100
/// let cls = MyClass::class();
101+
/// let obj = MyClass::alloc();
102+
/// ```
103+
///
104+
/// Implement the trait manually for a class with a lifetime parameter.
105+
///
106+
/// ```
107+
#[doc = include_str!("../examples/class_with_lifetime.rs")]
71108
/// ```
72109
pub unsafe trait ClassType: Message {
73110
/// The superclass of this class.

crates/objc2/src/encode/mod.rs

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
//! what Objective-C type-encodings are.
1010
//!
1111
//!
12-
//! ## Example
12+
//! ## Examples
1313
//!
1414
//! Implementing [`Encode`] and [`RefEncode`] for a custom type:
1515
//!
@@ -43,21 +43,33 @@
4343
//! assert!(MyStruct::ENCODING_REF.equivalent_to_str("^{MyStruct=fs}"));
4444
//! ```
4545
//!
46-
//! See the [`examples`] folder for more complex usage.
46+
//! Implementing [`Encode`] for a few core-graphics types.
4747
//!
48-
//! [`examples`]: https://github.com/madsmtm/objc2/tree/master/crates/objc2/examples
48+
//! Note that these are available in `icrate`, so the implementation here is
49+
//! mostly for demonstration.
4950
//!
51+
//! ```
52+
#![doc = include_str!("../../examples/encode_core_graphics.rs")]
53+
//! ```
54+
//!
55+
//! Implementing [`Encode`] and [`RefEncode`] for a transparent newtype.
56+
//!
57+
//! ```
58+
#![doc = include_str!("../../examples/encode_nsuinteger.rs")]
59+
//! ```
5060
//!
51-
//! ## Caveats
61+
//! Implementing [`RefEncode`] for an object, in this case `NSString`.
5262
//!
53-
//! We've taken the pragmatic approach with [`Encode`] and [`RefEncode`], and
54-
//! have implemented it for as many types as possible (instead of defining a
55-
//! bunch of subtraits for very specific purposes). However, that might
56-
//! sometimes be slightly surprising.
63+
//! ```
64+
#![doc = include_str!("../../examples/encode_nsstring.rs")]
65+
//! ```
5766
//!
58-
//! The primary example is [`()`][`unit`], which doesn't make sense as a
59-
//! method argument, but is a very common return type, and hence implements
60-
//! [`Encode`].
67+
//! Implementing [`RefEncode`] for a type where you don't necessarily know
68+
//! about the exact internals / the internals are not representable in Rust.
69+
//!
70+
//! ```
71+
#![doc = include_str!("../../examples/encode_opaque_type.rs")]
72+
//! ```
6173
6274
use core::cell::{Cell, UnsafeCell};
6375
use core::ffi::c_void;
@@ -82,6 +94,7 @@ pub use objc2_encode::{Encoding, EncodingBox, ParseError};
8294
/// If your type is an opaque type you should not need to implement this;
8395
/// there you will only need [`RefEncode`].
8496
///
97+
///
8598
/// # Safety
8699
///
87100
/// The type must be FFI-safe, meaning a C-compatible `repr` (`repr(C)`,
@@ -99,6 +112,7 @@ pub use objc2_encode::{Encoding, EncodingBox, ParseError};
99112
/// passed to Objective-C via. `objc2::msg_send!` their destructor will not be
100113
/// called!
101114
///
115+
///
102116
/// # Examples
103117
///
104118
/// Implementing for a struct:
@@ -149,6 +163,7 @@ pub unsafe trait Encode {
149163
/// - `Option<&mut T>`
150164
/// - `Option<NonNull<T>>`
151165
///
166+
///
152167
/// # Reasoning behind this trait's existence
153168
///
154169
/// External crates cannot implement [`Encode`] for pointers or [`Option`]s
@@ -159,6 +174,7 @@ pub unsafe trait Encode {
159174
/// Finally, having this trait allows for much cleaner generic code that need
160175
/// to represent types that can be encoded as pointers.
161176
///
177+
///
162178
/// # Safety
163179
///
164180
/// References to the object must be FFI-safe.

0 commit comments

Comments
 (0)