block2/lib.rs
1//! # Apple's C language extension of blocks
2//!
3//! C Blocks are functions which capture their environments, i.e. the
4//! C-equivalent of Rust's [`Fn`] closures. As they were originally developed
5//! by Apple, they're often used in Objective-C code. This crate provides
6//! capabilities to create, manage and invoke these blocks, in an ergonomic,
7//! "Rust-centric" fashion.
8//!
9//! At a high level, this crate contains four types, each representing
10//! different kinds of blocks, and different kinds of ownership.
11//!
12//! | `block2` type | Equivalent Rust type |
13//! | ---------------------------------------- | --------------------- |
14//! | `&Block<dyn Fn() + 'a>` | `&dyn Fn() + 'a` |
15//! | `RcBlock<dyn Fn() + 'a>` | `Arc<dyn Fn() + 'a>` |
16//! | `StackBlock<'a, (), (), impl Fn() + 'a>` | `impl Fn() + 'a` |
17//! | `GlobalBlock<dyn Fn()>` | [`fn` item] |
18//!
19//! For more information on the specifics of the block implementation, see the
20//! [C language specification][lang] and the [ABI specification][ABI].
21//!
22//! [lang]: https://clang.llvm.org/docs/BlockLanguageSpec.html
23//! [ABI]: http://clang.llvm.org/docs/Block-ABI-Apple.html
24//! [`fn` item]: https://doc.rust-lang.org/reference/types/function-item.html
25//!
26//!
27//! ## Using blocks
28//!
29//! You can create a new block from a closure using [`RcBlock::new`]. This can
30//! then be used to call functions or Objective-C methods that takes a block:
31//!
32//! ```
33//! use block2::RcBlock;
34//! #
35//! # struct ExampleObject;
36//! #
37//! # impl ExampleObject {
38//! # fn someMethod(&self, block: &block2::Block<dyn Fn(i32, i32) -> i32>) {
39//! # assert_eq!(block.call((5, 8)), 18);
40//! # }
41//! # }
42//! #
43//! # let obj = ExampleObject;
44//!
45//! let val = 5;
46//! let block = RcBlock::new(move |a, b| a + b + val);
47//! obj.someMethod(&block);
48//! ```
49//!
50//!
51//! ## My block isn't being run?
52//!
53//! Most of the time, blocks are used to do asynchronous work; but just like
54//! futures in Rust don't do anything unless polled, a lot of Apple APIs won't
55//! call your block unless a [run loop][run_loop] is active, see that link for
56//! more information on how to do so.
57//!
58//! [run_loop]: objc2::topics::run_loop
59//!
60//!
61//! ## Lifetimes
62//!
63//! When dealing with blocks, there can be quite a few lifetimes to keep in
64//! mind.
65//!
66//! The most important one is the lifetime of the block's data, i.e. the
67//! lifetime of the data in the closure contained in the block. This lifetime
68//! can be specified as `'f` in `&Block<dyn Fn() + 'f>`.
69//!
70//! Note that `&Block<dyn Fn()>`, without any lifetime specifier, can be a bit
71//! confusing, as the default depends on where it is typed. In function/method
72//! signatures, it defaults to `'static`, but as the type of e.g. a `let`
73//! binding, the lifetime may be inferred to be something smaller, see [the
74//! reference][ref-dyn-lifetime] for details. If in doubt, either add a
75//! `+ 'static` or `+ '_` to force an escaping or non-escaping block.
76//!
77//! Another lifetime is the lifetime of the currently held pointer, i.e. `'b`
78//! in `&'b Block<dyn Fn()>`. This lifetime can be safely extended using
79//! [`Block::copy`], so should prove to be little trouble (of course the
80//! lifetime still can't be extended past the lifetime of the captured data
81//! above).
82//!
83//! Finally, the block's parameter and return types can also contain
84//! lifetimes, as `'a` and `'r` in `&Block<dyn Fn(&'a i32) -> &'r u32>`.
85//! Unfortunately, these lifetimes are quite problematic and unsupported at
86//! the moment, due to Rust trait limitations regarding higher-ranked trait
87//! bounds. If you run into problems with this in a block that takes or
88//! returns a reference, consider using the ABI-compatible `NonNull<T>`, or
89//! transmute to a `'static` lifetime.
90//!
91//! [ref-dyn-lifetime]: https://doc.rust-lang.org/reference/lifetime-elision.html#default-trait-object-lifetimes
92//!
93//!
94//! ## Thread safety
95//!
96//! Thread-safe blocks are not yet representable in `block2`, and as such any
97//! function that requires a thread-safe block must be marked `unsafe`.
98//!
99//!
100//! ## Mutability
101//!
102//! Blocks are generally assumed to be shareable, and as such can only very
103//! rarely be made mutable.
104//!
105//! You will likely have to use interior mutability helpers like [`RefCell`]
106//! or [`Cell`] instead, see below.
107//!
108//! [`RefCell`]: core::cell::RefCell
109//! [`Cell`]: core::cell::Cell
110//!
111//!
112//! ### Transforming [`FnMut`] to a block
113//!
114//! Mutable closures differs from immutable ones in part in that they need to
115//! avoid re-entrancy.
116//!
117//! The below example transforms [`FnMut`] to [`Fn`] using a [`RefCell`]. We
118//! do not include this function as part of the public API of `block2`, as the
119//! specifics are very dependent on your use-case, and can be optimized with
120//! e.g. a [`Cell`] if your closure is [`Copy`] or if you do not care about
121//! unwind safety, or with [`UnsafeCell`] if you are able to unsafely
122//! guarantee the absence of re-entrancy.
123//!
124//! [`UnsafeCell`]: core::cell::UnsafeCell
125//!
126//! ```
127//! use std::cell::RefCell;
128//! use block2::RcBlock;
129//!
130//! fn fnmut_to_fn(closure: impl FnMut()) -> impl Fn() {
131//! let cell = RefCell::new(closure);
132//!
133//! move || {
134//! let mut closure = cell.try_borrow_mut().expect("re-entrant call");
135//! (closure)()
136//! }
137//! }
138//!
139//! let mut x = 0;
140//! let b = RcBlock::new(fnmut_to_fn(|| {
141//! x += 1;
142//! }));
143//! b.call(());
144//! b.call(());
145//! drop(b);
146//! assert_eq!(x, 2);
147//! ```
148//!
149//!
150//! ### Transforming [`FnOnce`] to a block
151//!
152//! [`FnOnce`] is similar to [`FnMut`] in that we must protect against
153//! re-entrancy, with the addition that it can also only be called once.
154//!
155//! Ensuring that it can be called once can be done by taking the closure
156//! out of an [`Option`] as shown in the example below. We can use [`Cell`]
157//! instead of [`RefCell`] here, since we never need to put the closure "back"
158//! for later use (like we need to do with `FnMut` above).
159//!
160//! In certain cases you may be able to do micro-optimizations, namely to use
161//! a [`ManuallyDrop`], if you wanted to optimize with the assumption that the
162//! block is always called, or [`unwrap_unchecked`] if you wanted to optimize
163//! with the assumption that it is only called once.
164//!
165//! [`Cell`]: core::cell::Cell
166//! [`ManuallyDrop`]: core::mem::ManuallyDrop
167//! [`unwrap_unchecked`]: core::option::Option::unwrap_unchecked
168//!
169//! ```
170//! use std::cell::Cell;
171//! use block2::RcBlock;
172//!
173//! fn fnonce_to_fn(closure: impl FnOnce()) -> impl Fn() {
174//! let cell = Cell::new(Some(closure));
175//! move || {
176//! let closure = cell.take().expect("called twice");
177//! closure()
178//! }
179//! }
180//!
181//! let v = vec![1, 2, 3];
182//! let b = RcBlock::new(fnonce_to_fn(move || {
183//! drop(v);
184//! }));
185//! b.call(());
186//! ```
187//!
188//!
189//! ## External functions using blocks
190//!
191//! To declare external functions or methods that takes blocks, use
192//! `&Block<dyn Fn(Params) -> R>` or `Option<&Block<dyn Fn(Args) -> R>>`,
193//! where `Params` is the parameter types, and `R` is the return type.
194//!
195//! For this example, consider the function `check_addition` which takes a
196//! single parameter, namely a block that adds two integers, and then checks
197//! that the addition is correct.
198//!
199//! Such a function could be written in C like in the following.
200//!
201//! ```objc
202//! #include <cassert>
203//! #include <stdint.h>
204//! #include <Block.h>
205//!
206//! void check_addition(int32_t (^block)(int32_t, int32_t)) {
207//! assert(block(5, 8) == 13);
208//! }
209//! ```
210//!
211//! An `extern "C" { ... }` declaration for that function would then be:
212//!
213//! ```
214//! use block2::Block;
215//!
216//! extern "C" {
217//! fn check_addition(block: &Block<dyn Fn(i32, i32) -> i32>);
218//! }
219//! ```
220//!
221//! This can similarly be done for Objcective-C methods declared with
222//! [`objc2::extern_methods!`] (though most of the time, the [framework
223//! crates][framework-crates] will take care of that for you).
224//!
225//! ```
226//! use block2::Block;
227//! use objc2::extern_methods;
228//! #
229//! # use objc2::ClassType;
230//! # objc2::extern_class!(
231//! # #[unsafe(super(objc2::runtime::NSObject))]
232//! # #[name = "NSObject"]
233//! # struct MyClass;
234//! # );
235//!
236//! impl MyClass {
237//! extern_methods!(
238//! #[unsafe(method(checkAddition:))]
239//! pub fn checkAddition(&self, block: &Block<dyn Fn(i32, i32) -> i32>);
240//! );
241//! }
242//! ```
243//!
244//! If the function/method allows passing `NULL` blocks, the type should be
245//! `Option<&Block<dyn Fn(i32, i32) -> i32>>` instead.
246//!
247//! [framework-crates]: objc2::topics::about_generated
248//!
249//!
250//! ## Invoking blocks
251//!
252//! We can also define the external function in Rust, and expose it to
253//! Objective-C. To do this, we can use [`Block::call`] to invoke the block
254//! inside the function.
255//!
256//! ```
257//! use block2::Block;
258//!
259//! #[no_mangle]
260//! extern "C" fn check_addition(block: &Block<dyn Fn(i32, i32) -> i32>) {
261//! assert_eq!(block.call((5, 8)), 13);
262//! }
263//! ```
264//!
265//! Note the extra parentheses in the `call` method, since the arguments must
266//! be passed as a tuple.
267//!
268//!
269//! ## Specifying a runtime
270//!
271//! Different runtime implementations exist and act in slightly different ways
272//! (and have several different helper functions), the most important aspect
273//! being that the libraries are named differently, so we must take that into
274//! account when linking.
275//!
276//! You can choose the desired runtime by using the relevant cargo feature
277//! flags, see the following sections.
278//!
279//!
280//! ### Apple's [`libclosure`](https://github.com/apple-oss-distributions/libclosure)
281//!
282//! This is the most common and most sophisticated runtime, and it has quite a
283//! lot more features than the specification mandates.
284//!
285//! The minimum required operating system versions are as follows (though in
286//! practice Rust itself requires higher versions than this):
287//!
288//! - macOS: `10.6`
289//! - iOS/iPadOS: `3.2`
290//! - tvOS: `1.0`
291//! - watchOS: `1.0`
292//!
293//! **This is used by default**, so you do not need to specify a runtime if
294//! you're using this crate on of these platforms.
295//!
296//!
297//! ### LLVM `compiler-rt`'s [`libBlocksRuntime`](https://github.com/llvm/llvm-project/tree/release/13.x/compiler-rt/lib/BlocksRuntime)
298//!
299//! - Feature flag: `compiler-rt`.
300//!
301//! This is a copy of Apple's older (around macOS 10.6) runtime, and is now
302//! used in [Swift's `libdispatch`] and [Swift's Foundation] as well.
303//!
304//! The runtime and associated headers can be installed on many Linux systems
305//! with the `libblocksruntime-dev` package.
306//!
307//! Using this runtime probably won't work together with `objc2` crate.
308//!
309//! [Swift's `libdispatch`]: https://github.com/apple/swift-corelibs-libdispatch/tree/swift-5.5.1-RELEASE/src/BlocksRuntime
310//! [Swift's Foundation]: https://github.com/apple/swift-corelibs-foundation/tree/swift-5.5.1-RELEASE/Sources/BlocksRuntime
311//!
312//!
313//! ### GNUStep's [`libobjc2`](https://github.com/gnustep/libobjc2)
314//!
315//! - Feature flag: `gnustep-1-7`, `gnustep-1-8`, `gnustep-1-9`, `gnustep-2-0`
316//! and `gnustep-2-1` depending on the version you're using.
317//!
318//! GNUStep is a bit odd, because it bundles blocks support into its
319//! Objective-C runtime. This means we have to link to `libobjc`, and this is
320//! done by depending on the `objc2` crate. A bit unorthodox, yes, but it
321//! works.
322//!
323//! Sources:
324//!
325//! - [`Block.h`](https://github.com/gnustep/libobjc2/blob/v2.1/objc/blocks_runtime.h)
326//! - [`Block_private.h`](https://github.com/gnustep/libobjc2/blob/v2.1/objc/blocks_private.h)
327//!
328//!
329//! ### Microsoft's [`WinObjC`](https://github.com/microsoft/WinObjC)
330//!
331//! - Feature flag: `unstable-winobjc`.
332//!
333//! **Unstable: Hasn't been tested on Windows yet!**
334//!
335//! [A fork](https://github.com/microsoft/libobjc2) based on GNUStep's `libobjc2`
336//! version 1.8.
337//!
338//!
339//! ### [`ObjFW`](https://github.com/ObjFW/ObjFW)
340//!
341//! - Feature flag: `unstable-objfw`.
342//!
343//! **Unstable: Doesn't work yet!**
344//!
345//!
346//! ## C Compiler configuration
347//!
348//! To our knowledge, only Clang supports blocks. To compile C or Objective-C
349//! sources using these features, you should set [the `-fblocks` flag][flag].
350//!
351//! [flag]: https://clang.llvm.org/docs/ClangCommandLineReference.html#cmdoption-clang-fblocks
352
353#![no_std]
354#![allow(rustdoc::broken_intra_doc_links)] // FIXME link to objc2::topics
355#![warn(missing_docs)]
356#![warn(missing_debug_implementations)]
357#![warn(clippy::missing_errors_doc)]
358#![warn(clippy::missing_panics_doc)]
359// Update in Cargo.toml as well.
360#![doc(html_root_url = "https://docs.rs/block2/0.6.2")]
361#![cfg_attr(docsrs, feature(doc_cfg))]
362#![cfg_attr(feature = "unstable-coerce-pointee", feature(derive_coerce_pointee))]
363
364#[cfg(not(feature = "alloc"))]
365compile_error!("The `alloc` feature currently must be enabled.");
366
367extern crate alloc;
368#[cfg(feature = "std")]
369extern crate std;
370
371#[cfg(all(
372 not(docsrs),
373 not(any(
374 target_vendor = "apple",
375 feature = "compiler-rt",
376 feature = "gnustep-1-7",
377 feature = "unstable-objfw",
378 ))
379))]
380compile_error!("`block2` only works on Apple platforms. Pass `--target aarch64-apple-darwin` or similar to compile for macOS.\n(If you're absolutely certain that you want to use `block2` on Linux/Windows, you can specify that with the `gnustep-x-y`/`compiler-rt` Cargo features instead).");
381
382#[cfg(any(
383 all(feature = "compiler-rt", feature = "gnustep-1-7"),
384 all(feature = "gnustep-1-7", feature = "unstable-objfw"),
385 all(feature = "compiler-rt", feature = "unstable-objfw"),
386))]
387compile_error!("Only one runtime may be selected");
388
389#[cfg(feature = "unstable-objfw")]
390compile_error!("ObjFW is not yet supported");
391
392// Link to `libclosure` (internally called `libsystem_blocks.dylib`), which is
393// exported by `libSystem.dylib`.
394//
395// Note: Don't get confused by the presence of `System.framework`, it is a
396// deprecated wrapper over the dynamic library, so we'd rather use the latter.
397//
398// Alternative: Only link to `libsystem_blocks.dylib`:
399// println!("cargo:rustc-link-search=native=/usr/lib/system");
400// println!("cargo:rustc-link-lib=dylib=system_blocks");
401#[cfg_attr(
402 all(
403 target_vendor = "apple",
404 not(feature = "compiler-rt"),
405 not(feature = "gnustep-1-7"),
406 not(feature = "unstable-objfw"),
407 not(feature = "std"), // `libSystem` is already linked by `libstd`.
408 ),
409 link(name = "System", kind = "dylib")
410)]
411// Link to `libBlocksRuntime`.
412#[cfg_attr(feature = "compiler-rt", link(name = "BlocksRuntime", kind = "dylib"))]
413// Link to `libobjfw`, which provides the blocks implementation.
414#[cfg_attr(feature = "unstable-objfw", link(name = "objfw", kind = "dylib"))]
415extern "C" {}
416
417// Don't link to anything on GNUStep; objc2 already does that for us!
418//
419// We do want to ensure linkage actually happens, though.
420#[cfg(feature = "gnustep-1-7")]
421extern crate objc2 as _;
422
423mod abi;
424mod block;
425mod debug;
426mod encoding;
427pub mod ffi;
428mod global;
429mod rc_block;
430mod stack;
431mod traits;
432
433pub use self::block::Block;
434pub use self::global::GlobalBlock;
435pub use self::rc_block::RcBlock;
436pub use self::stack::StackBlock;
437pub use self::traits::{BlockFn, IntoBlock, ManualBlockEncoding};
438
439/// Deprecated alias for a `'static` `StackBlock`.
440#[deprecated = "renamed to `StackBlock`"]
441pub type ConcreteBlock<A, R, Closure> = StackBlock<'static, A, R, Closure>;
442
443// Note: We could use `_Block_object_assign` and `_Block_object_dispose` to
444// implement a `ByRef<T>` wrapper, which would behave like `__block` marked
445// variables and have roughly the same memory management scheme as blocks.
446//
447// But I've yet to see the value in such a wrapper in Rust code compared to
448// just using `Box`, `Rc` or `Arc`, and since `__block` variables are
449// basically never exposed as part of a (public) function's API, we won't
450// implement such a thing yet.
451
452/// Helper type to allow changing [`Block`] in the future without affecting
453/// framework crates.
454///
455/// Tracked in [#572](https://github.com/madsmtm/objc2/issues/572).
456pub type DynBlock<F> = crate::Block<F>;