memcond 0.1.0

Assertion memoizer. Safely bring the outcome of an assertion next the code where it is actually useful.
Documentation
  • Coverage
  • 44.44%
    4 out of 9 items documented1 out of 1 items with examples
  • Size
  • Source code size: 23.94 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 1.94 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 19s Average build duration of successful builds.
  • all releases: 19s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • igankevich/memcond
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • igankevich

This crate provide a [memcond] macro that memoizes the outcome of an assertion and safely repeats this assertion when the variables used in the assertion are actually used.

use core::hint::black_box;
use memcond::memcond;

const NUM_PAGES: usize = 100;

memcond! {
    // This condition is checked in `PageNumber::new`.
    const fn is_page_number(number: usize) -> bool {
        number < 100
    }

    #[derive(Clone, Copy)]
    pub struct PageNumber;
}

// The macro generates roughly the following structure.
//
// ```rust
// pub struct PageNumber {
//     number: usize,
// }
// 
// impl PageNumber {
//     pub const fn new(number: usize) -> Result<Self, usize>
//     where
//         usize: memcond::Freeze
//     {
//         if !(number < 100) {
//             return Err(number);
//         }
//         Ok(Self { number })
//     }
// 
//     pub const fn number(self) -> usize {
//         // SAFETY: It's safe because condition arguments are internally immutable
//         // (all implement `Freeze`) and can't be changed via `impl PageNumber`.
//         unsafe { assert_unchecked(self.number < 100) };
//         self.number
//     }
// }
// ```

// Here the condition is checked and memoized.
let page = black_box(PageNumber::new(33).unwrap());
// A simple bitset for demonstration.
let mut pages = [false; NUM_PAGES];
// Here the condition is enforced again via `assert_unchecked` with no overhead
// because the outcome of the assertion was memoized.
// This line can be anywhere in the code with respect to `PageNumber::new`.
pages[page.number()] = true;
// As a result no index bounds checking code is generated by the compiler.

The structure is placed in its own module (not shown in the example for simplicity) to prevent implementations from accessing its fields and potentially changing the outcome of the condition. Immutability of the condition arguments is enforced via [Freeze]. Condition itself is const fn and forbids any unsafe code.

[memcond] expected condition arguments to be copiable. If this is not posible, then [memcond_ref] might be an option: this macro expects condition arguments to be passed by reference and generates into_inner method that allows one to extract the arguments without copying.