#defer #deferred #scope #execution #alloc #statement #zig

no-std defer-heavy

A versatile and easy to use defer statement for Rust. Similar to Go's or Zig's defer.

1 unstable release

0.1.0 Oct 4, 2024

#483 in Rust patterns

Download history 64/week @ 2024-09-28 37/week @ 2024-10-05 895/week @ 2024-10-12 377/week @ 2024-10-19 57/week @ 2024-10-26 200/week @ 2024-11-02

1,529 downloads per month
Used in 2 crates

MIT license

20KB
167 lines

A versatile and easy to use defer statement for Rust. Similar to Go's or Zig's defer.

Loosely based on and inspired by: https://github.com/rodrigocfd/defer-lite

This crate is compatible uses no_std. The default features use alloc. To disable alloc set default-features to false in cargo.toml.

This crates provides 6 macros for different use cases of deferment:

  1. defer! simple deferment. Will execute when current scope ends.

    • If this is all you need then use the defer-lite crate!
  2. defer_move! same as defer! but moves local variables into the closure.

  3. defer_guard! Returns a guard that causes execution when its scope ends.

    • Execution can be canceled or preempted.
  4. defer_move_guard! Same as defer_guard! but moves local variables into the closure.

  5. defer_arc! Returns a reference counted guard than can be shared with other threads.

    • Execution can be canceled or preempted.
    • Closure must be Send
    • Target must support Arc & AtomicBool.
    • Target must support alloc
    • can be disabled with default-features=false in Cargo.toml
  6. defer_move_arc! Same as defer_arc! but moves local variables into the closure.

    • All used local variables must be Send.

Usage

Add the dependency in your Cargo.toml:

[dependencies]
defer-heavy = "0.1.0"

Examples

Simple Defer

If this is all you need use the defer-lite crate instead!

use defer_heavy::defer;

fn main() {
    defer! { println!("Second"); }
    println!("First");
}

Canceled Defer

use defer_heavy::defer_guard;

fn main() {
    let defer1 = defer_guard! { unreachable!("Wont be executed"); };
    let defer2 = defer_guard! { println!("Second"); };
    let defer3 = defer_guard! { println!("Fourth"); };

    println!("First");
    defer2.destroy(); //Same as drop(defer2);
    println!("Third");
    defer1.cancel();
}

Reference Counted Defer

use std::thread;
use std::time::Duration;
use defer_heavy::defer_move_arc;

pub fn main() {
    let deferred = defer_arc! { println!("Executed in {:?}", thread::current().id());};
    println!("Main thread {:?}", thread::current().id());
    {
        let deferred = deferred.clone();
        thread::spawn(move ||{
            println!("Spawned thread {:?}", thread::current().id());
            let _deferred = deferred.own();
            thread::sleep(Duration::from_millis(2000)); //SIMULATE work
       });
   }
   thread::sleep(Duration::from_millis(2000)); //SIMULATE WORK
 }

Prints:

Main thread Thread(1)
Spawned thread Thread(2)
"Executed in Thread(1)" or "Executed in Thread(2)"

Order of execution

Rust guarantees that the order in which the closures are dropped (and therefore executed) are in reverse order of creation. This means the last defer! in the scope executes first.

No runtime deps

Features