3 个版本 (破坏性更新)
0.3.0 | 2020年6月6日 |
---|---|
0.2.0 | 2019年11月5日 |
0.1.0 | 2019年11月4日 |
在 #invariants 中排名 37
每月下载 25 次
在 rep 中使用
22KB
334 行
rep
rep
是一个小工具,允许您轻松地在Rust数据结构中强制执行 表示/类不变性。
表示不变性是针对您的数据结构每次突变都必须为真的逻辑断言。例如,在您的GIS应用程序中,您可能有一个如下的rep不变性。
self.lat >= -90.0 && self.lat <= 90 && self.long >= -180.0 && self.long <= 180
使用 rep
强制执行表示不变性非常简单。向数据结构添加不变性只需要两个简单步骤。
- 定义正确的表示(通过手动实现或宏实现
CheckRep
) - 插入运行时检查(通过手动实现或宏实现)
一些示例
我们可以从一个简单的数据结构开始。
use rep::*;
pub struct Line {
x1: i32,
y1: i32,
x2: i32,
y2: i32
}
可以实现对 CheckRep
特质的实现。这充当了正确表示的定义。
impl CheckRep for Line {
fn is_correct(&self) -> bool {
self.x1 != self.x2 && self.y1 != self.y2
}
}
现在,我们可以使用 #[check_rep]
宏来自动在所有公开的、修改 &mut self
的方法开始和结束处插入对 check_rep
的调用。我们也可以在需要的地方手动调用 check_rep
。
#[check_rep] // <-- this inserts calls to check_rep at start and end of move_by
impl Line {
pub fn new() -> Self {
let new_line = Self {
x1: -1,
y1: -1,
x1: 1,
y1: 1
};
new_line.check_rep();
new_line
}
pub fn move_by(&mut self, x: i32, y: i32) {
self.x1 += x;
self.x2 += x;
self.y1 += y;
self.y2 += y;
}
}
更多示例
对于简单的表示,我们甚至可以通过派生实现来获得 CheckRep
的实现。
#[derive(CheckRep)]
struct Circle {
x: i32,
y: i32,
#[rep(assert_gt = 0)]
#[rep(assert_le = 2048)]
r: i32,
}
struct Parser {
#[rep(assert_default)]
unclosed_delims: (usize, usize, usize) // this is representing (parens, braces, brackets)
}
我们可以递归检查表示并使用每个字段的自定义函数。
fn is_health_valid(h: u32) -> bool {
h > 0 && h < 100
}
#[derive(CheckRep)]
struct Player {
#[rep(check)]
position: Point,
#[rep(assert_with = "is_health_valid")]
health: u32
}
更高级的rep检查可以通过自定义检查来实现。
fn is_health_valid(h: u32) -> bool {
h > 0 && h < 100
}
#[derive(CheckRep)]
struct Player {
#[rep(use_custom)] // indicates that custom code should be used
#[rep(check)]
position: Point,
#[rep(assert_with = "is_health_valid")]
health: u32
}
impl CustomCheckRep for Line {
fn c_correctness(&self) -> Result<(), Vec<String>> {
let mut errors = vec![];
if self.x2 != self.y2 {
errors.push(String::from("self.x2 must equal self.y2"));
}
if errors.len() == 0 { Ok(()) } else { Err(errors) }
}
}
struct Player {
position: Point,
health: u32
}
impl CheckRep for Player {
fn correctness(&self) -> Result<(), Vec<String>> {
let mut errors = vec![];
// your code here...
if errors.len() == 0 { Ok(()) } else { Err(errors) }
}
}
一旦实现了 CheckRep
,您可以使用它与宏 #[check_rep
,#[require_rep
和 #[check_rep
。
// this adds `check_rep` at start and end of all public mutating methods
#[check_rep]
impl Device {
pub fn turn_on(&mut self) {}
// require_rep, ensure_rep, check_rep add to start, end, start and end respectively
#[require_rep]
pub fn get_voltage(&mut self, p: Position) {}
#[ensure_rep]
pub fn actuate(&mut self, p: Position, v: Voltage) {}
#[check_rep]
fn do_something(&self) {}
}
如果有记录器存在,则将记录违反不变性,而不是崩溃。
用法
只需将以下内容添加到您的 Cargo.toml
文件中。
[dependencies]
rep = "0.3.0"
然后,在您的模块中。
use rep::*;
依赖项
~1.5MB
~35K SLoC