scoped-callback 0.2.0

Register scoped functions with local references to 'static lifetime callbacks in a safe manner
Documentation

Docs

scoped-callback

Allows registering scoped functions with local borrows with code that expect functions taking 'static lifetimes.

Motivating example:

/// Function for registering a callback with a `'static` lifetime.
fn register(callback: Box<dyn FnMut(i32)>) -> Box<dyn FnMut(i32)> {
  callback
}
/// Function for de-registering the handle returned by `register`,
/// in this case the callback itself.
fn deregister(_callback: Box<dyn FnMut(i32)>) {}

/// Variable that can be borrowed from inside the callback closure
let a = 42;

/// After returning from the closure, `scope` guarantees that any callbacks
/// that have not yet been de-registered are de-registered.
scope(|scope| {

  /// Register the given closure, which can borrow from the stack outside `scope`
  /// using the `register` and `deregister` functions declared above.
  /// The returned handle will cause a de-register when dropped.
  let _registered = scope.register(
    |_| {
      let b = a * a;
      println!("{}", b);
    },
    register,
    deregister,
  );
});

See scope_async and scope_async_local as well for versions that work with async scopes.

How is this safe?

There are three important concepts in this implementation:

  • register returns a Registered instance, which when Drop-ed causes the callback to be de-registered using the provided function.
  • In case the Registered instance is not Drop-ed, for example by calling std::mem::forget (which is not unsafe!) the de-registering using the provided function will instead happen after leaving the closure passed to scope.
  • In case the given de-register function doesn't actually de-register the callback, and for some reason the callback given to the register function is called after the closure passed to scope, the call will cause a panic!.

no_std

This crate supports no_std by disabling its std feature.

License: Apache-2.0