Kurz, wahrscheinlich hilfreich Antwort: Es gibt die block
Kiste, die aussieht, als könnte es den Job tun.
Kurze, nicht hilfreiche Antwort: Soweit ich weiß, hat Rust keine Unterstützung für Apples Block-Erweiterung. Es gibt keinen äquivalenten Rust-Typ, vorausgesetzt, Sie möchten eine API aufrufen, die einen Block erwartet.
Länger, geringfügig weniger nicht hilfreich Antwort: Von dem, was ich aus some Clang documentation on the Apple Block ABI sammeln kann, void(^)(void)
würde die gleiche Größe wie ein normalen Zeiger sein.
Als solche ist mein Rat wie folgt: behandle Blöcke als opak, Zeiger-Größe Werte. Um einen aufzurufen, schreibe eine Funktion in C, die sie für dich aufruft.
Das Folgende ist nicht getestet (ich habe keinen Mac), aber sollte zumindest in die richtige Richtung gehen. Außerdem markiere ich dieses Community-Wiki so, dass jeder, der testen kann, es reparieren kann, wenn es nötig ist.
In Rust:
// These are the "raw" representations involved. I'm not using std::raw
// because that's not yet stabilised.
#[deriving(Copy, Clone)]
struct AppleBlock(*const());
#[deriving(Copy, Clone)]
struct RustClosure(*const(), *const());
// Functions that we need to be written in C:
extern "C" {
fn rust_closure_to_block(closure_blob: RustClosure) -> AppleBlock;
fn block_release(block_blob: AppleBlock);
}
// The function that the C code will need. Note that this is *specific* to
// FnMut() closures. If you wanted to generalise this, you could write a
// generic version and pass a pointer to that to `rust_closure_to_block`.
extern "C" fn call_rust_closure(closure_blob: RustClosure) {
let closure_ref: &FnMut() = unsafe { mem::transmute(closure_blob) };
closure_ref();
}
// This is what you call in order to *temporarily* turn a closure into a
// block. So, you'd use it as:
//
// with_closure_as_block(
// || do_stuff(),
// |block| CFRunLoopPerformBlock(fl, mode, block)
// );
fn with_closure_as_block<C, B, R>(closure: C, body: B) -> R
where C: FnMut(), B: FnOnce(block_blob) -> R {
let closure_ref: &FnMut() = &closure;
let closure_blob: RustClosure = unsafe { mem::transmute(closure_ref) };
let block_blob = unsafe { rust_closure_to_block(closure_blob) };
let r = body(block_blob);
unsafe { block_release(block_blob) };
r
}
In C:
typedef struct AppleBlock {
void *ptr;
} AppleBlock;
typedef struct RustClosure {
void *ptr;
void *vt;
} RustClosure;
void call_rust_closure(RustClosure closure_blob);
AppleBlock rust_closure_to_block(RustClosure closure_blob) {
return (AppleBlock)Block_copy(^() {
call_rust_closure(closure_blob);
});
}
// I'm not using Block_release directly because I don't know if or how
// blocks change name mangling or calling. You might be able to just
// use Block_release directly from Rust.
void block_release(AppleBlock block) {
Block_release((void (^)(void))block);
}