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:
pre
-- runs once beforebody
.body
-- the main part of the function. This is the reason the function exists!post
-- runs once afterbody
.double_check
-- runs twice; afterpre
, and beforepost
.
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:
pre
double_check
of thecontract!
blockdouble_check
of thefn
definitionbody
double_check
of thecontract!
blockdouble_check
of thefn
definitionpost
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(); } } } }