Crate on_demand[][src]

This crate provides a macro to generate aux macros for on-demand usage.

Examples

use on_demand::generate_on_demand_macro;

fn foo() {
    generate_on_demand_macro!(a: usize = None, {
        println!("a");
        1
    });
    generate_on_demand_macro!(b: usize = None, {
        println!("b");
        let a_data = on_demand_get_a!();
        2 + *a_data
    });
    generate_on_demand_macro!(c: usize = None, {
        println!("c");
        let a_data = on_demand_get_a!();
        let b_data = on_demand_get_b!();
        3 + *a_data + *b_data
    });

    let c_data = on_demand_get_c!();
    assert_eq!(*c_data, 6);
}

After calling generate_on_demand_macro to the variable (for example, a), three new macros on_demand_get_a, on_demand_get_a_mut and on_demand_into_a are generated. When on_demand_get_a is called, it determines whether a has been calculated. If it is, then returns the reference to its data, otherwise calls the expression given as the second parameter of generate_on_demand_macro, then assigns the returned value to a. The other two generated macros do the similar job, but returns the mutable reference to, or takes ownership of a.

In all, this means calculating a lazily using the expression.

Notes

The returned value of on_demand_get_a is essentially a Ref, and on_demand_get_a_mut is RefMut. So remember to dereference it when necessary.

When calling this macro, we assign a default value by generate_on_demand_macro!(a: MyType = default_value_of_optional_my_type), { /* block */ });. The default_value_of_optional_my_type should be of type Option<MyType>. If it is not None, then a is considered calculated already, and the expression given in block will never be called.

There are some crates providing lazy feature, such as spin. However, those methods are based on closure, which could not handle some situations such as below (error handlings are omitted for simplicity):

fn foo<T: Seek + Read>(binary: &mut T) {
    generate_on_demand_macro!(a: u32 = None, {
        let mut buf = [0; 4];
        binary.seek(SeekFrom::Start(0)).unwrap();
        binary.read(&mut buf).unwrap();
        u32::from_be_bytes(buf)
    });
    generate_on_demand_macro!(b: u32 = None, {
        let a_data = on_demand_get_a!();
        let mut buf = [0; 4];
        binary.seek(SeekFrom::Start(0)).unwrap();
        binary.read(&mut buf).unwrap();
        u32::from_be_bytes(buf) + *a_data
    });
    generate_on_demand_macro!(c: u32 = None, {
        let a_data = on_demand_get_a!();
        let b_data = on_demand_get_b!();
        let mut buf = [0; 4];
        binary.seek(SeekFrom::Start(0)).unwrap();
        binary.read(&mut buf).unwrap();
        u32::from_be_bytes(buf) + *a_data + *b_data
    });
    let a_data = on_demand_get_a!();
    let b_data = on_demand_get_b_mut!();
    drop(b_data); // drop here since c will take ownership
    let c_data = on_demand_into_c!();
}

binary is considered as uniquely borrowed if closure is used, then the borrow checker won't allow us do above things. However, macros can do such things.

Macros

generate_on_demand_macro

Macro to generate on-demand macro