Skip to main content

Crate memcond

Crate memcond 

Source
Expand description

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.

Macros§

memcond
This macro generates a struct that checks the provided condition in the constructor and enforces this condition via assert_unchecked in each getter.
memcond_ref
Same as memcond but expects condition arguments to be passed by reference.

Traits§

Freeze
This trait marks types that are internally immutable.