Macro adhesion::contract [] [src]

macro_rules! contract {
    (
        @muncher,
        [double_check $double_check: tt],
        $(#[$attribute: meta])*
        $(pub$(($access_modifier: ident))*)* fn $fn_name: ident $($tail: tt)*
    ) => { ... };
    (
        @muncher,
        [double_check $_old_double_check: tt],
        double_check $double_check: tt
        $($tail: tt)+
    ) => { ... };
    (
        @muncher,
        [double_check $_old_double_check: tt],
    ) => { ... };
    (
        $($tail: tt)*
    ) => { ... };
}

Converts one or more fn definitions inside to be contracted functions that may have pre- and post-condition checks. The following blocks are valid inside of a fn definition:

  1. pre -- runs once before body.
  2. body -- the main part of the function. This is the reason the function exists!
  3. post -- runs once after body.
  4. double_check -- runs twice; after pre, and before post.

A double_check block may be used at the top level of a contract! invocation, which will be used by ALL fn definitions inside. This block is particularly useful for structs, where invariants for all data members may need to be maintained across method calls.

When every contract block is being utilized, the final order of the checks inserted into the contract definition are as follows:

  1. pre
  2. double_check of the contract! block
  3. double_check of the fn definition
  4. body
  5. double_check of the contract! block
  6. double_check of the fn definition
  7. post

No blocks in this macro are required, nor is any specific order required.

It should be noted that conditional compilation is NOT handled by this library, and that if conditional compilation is desired, cfg statements should be used like with any most other Rust code.

Examples

contract! {
    fn asdf(asda: bool, stuff: u64) -> bool {
        pre {
            assert!(stuff < 30, "pre-condition violation");
        }
        body {
            asda
        }
        post(return_value) {
            assert!(return_value == (stuff % 3 == 0), "post-condition violation");
        }
        double_check {
            assert!(stuff > 5, "double_check violation");
        }
    }
}

assert_that!(asdf(true, 7), panics); // post failure
assert_that!(asdf(true, 64), panics); // pre failure
assert_that!(asdf(false, 3), panics); // double_check failure
asdf(true, 6);
asdf(false, 7);
asdf(false, 11);
asdf(true, 24);
struct Counter {
    count: u32,
    max: u32,
}

impl Counter {
    contract! {
        double_check {
            assert!(self.count <= self.max);
        }

        fn tick_up(&mut self) {
            body {
                // Force a panic if this overflows, even in release
                self.count = self.count.checked_add(1).unwrap();
            }
        }

        fn tick_down(&mut self) {
            body {
                // Force a panic if this underflows, even in release
                self.count = self.count.checked_sub(1).unwrap();
            }
        }
    }
}