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