| Crates.io | invariant-reference |
| lib.rs | invariant-reference |
| version | 0.1.0 |
| created_at | 2025-12-07 18:47:40.097722+00 |
| updated_at | 2025-12-07 18:47:40.097722+00 |
| description | Helps code authors define and use invariants |
| homepage | |
| repository | https://github.com/dmitryvk/invariant-reference |
| max_upload_size | |
| id | 1972067 |
| size | 10,563 |
When writing Rust code that contains .expect() calls, we need to provide a message that explains
why the panic is impossible here, e.g.:
fn main() {
let items = make_items();
let first = items.get(0).expect("`items` is initialized to 3 items");
}
fn make_items() -> Vec<i32> {
vec![1, 2, 3]
}
The use of string message for .expect() is not ideal, as its contents is not checked by the compiler,
and it is hard to provide a compact message with a comprehensive explanation.
We can make some important observations:
.expect() call is actually a use of some invariantE.g., for the example code:
make_items returns a non-empty vector"make_items functionmain functionThis library aims to improve this by providing at least some of degree of compiler checking for .expect() calls.
We let the Rust's type system to check that all used references are proven in some other place of code.
With the help of invariant_reference, the example code is written as:
fn main() {
let items = make_items();
let first = items.get(0).unwrap_under_invariant::<MakeItemsReturnsNonEmpty>();
}
#[derive(Invariant)]
#[invariant(message = "make_items() returns non-empty vector")]
struct MakeItemsReturnsNonEmpty;
fn make_items() -> Vec<i32> {
invariant_established!(MakeItemsReturnsNonEmpty, why = "vec with 3 items is non-empty");
vec![1, 2, 3]
}
This style of code has several advantages:
invariant_established! is placed close to the code that ensures that the invariant holds;
during code review it is easier to spot mistakes in such codeinvariant_reference and invariant_reference_derive crates#[derive(Invariant)] macro:
#[derive(Invariant)]
#[invariant(message = "the xyz vector is not empty", num_proofs = 1)]
struct XyzVecIsNotEmpty;
message that explains the essence of the invariant, i.e. what condition must hold truenum_proofs attribute.invariant_established!(XyzVecIsNotEmpty, why = "...") macro in the specific code place where it makes sense
(e.g., in the body of a struct constructor)
invariant_established!(XyzVecIsNotEmpty[0], why = "...") macro if there are multiple such placesOptionExt::unwrap_under_invariant::<XyzVecIsNotEmpty>() or ResultExt::unwrap_under_invariant::<XyzVecIsNotEmpty>() instead of .expect()invariant_established! is not invoked (even if the invariant is not referenced) or invoked multiple times for the same invariant (unless using different indices)invariant_established! may only be called within the same crate, as this libraries relies on traits.