Design By Contract for Rust
Annotate functions and methods with "contracts", using invariants, pre-conditions and post-conditions.
Design by contract is a popular method to augment code with formal interface specifications. These specifications are used to increase the correctness of the code by checking them as assertions at runtime.
(For a more complete example see the RangedInt test)
Modes
All the attributes (pre, post, invariant) have debug_* and test_* versions.
-
debug_pre/debug_post/debug_invariantusedebug_assert!internally rather thanassert! -
test_pre/test_post/test_invariantguard theassert!with anif cfg!(test). This should mostly be used for stating equivalence to "slow but obviously correct" alternative implementations or checks.For example, a merge-sort implementation might look like this
Set-up
To install the latest version, add contracts to the dependency section of the Cargo.toml file.
[dependencies]
contracts = "0.2.0"
To then bring all procedural macros into scope, you can add use contracts::*; in all files you plan
to use the contract attributes.
Alternatively use the "old-style" of importing macros to have them available in project-wide.
extern crate contracts;
Configuration
This crate exposes a number of feature flags to configure the assertion behavior.
disable_contracts- disables all checks and assertions.override_debug- changes all contracts (excepttest_ones) intodebug_*versionsoverride_log- changes all contracts (excepttest_ones) into alog::error!()call if the condition is violated. No abortion happens.
TODOs
- implement more contracts for traits.
- add a static analyzer à la SPARK for whole-projects using the contracts to make static assertions.