3个版本 (破坏性)
0.3.0 | 2020年6月6日 |
---|---|
0.2.0 | 2019年11月5日 |
0.1.0 | 2019年11月4日 |
#754 in 数据结构
10KB
79 行
rep
rep
是一个小型实用程序,允许您轻松地在Rust数据结构中强制执行 表示/类不变性。
表示不变性是针对您数据结构的每个变异都必须为真的逻辑断言。例如,在您的GIS应用程序中,您可能有一个针对 LatLong
的以下 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]
宏自动在所有 pub
和修改 &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
约37K SLoC