├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── examples └── serialization.rs └── src └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | # sudo is required to enable kcov to use the personality syscall 3 | sudo: required 4 | dist: trusty 5 | cache: cargo 6 | 7 | rust: 8 | - nightly 9 | - beta 10 | - stable 11 | 12 | before_script: 13 | - eval git pull --rebase https://github.com/Geal/mutguard master 14 | - eval git log --pretty=oneline HEAD~5..HEAD 15 | 16 | matrix: 17 | include: 18 | - rust: nightly 19 | before_script: 20 | - export PATH=$HOME/.cargo/bin:$PATH 21 | - cargo install cargo-update || echo "cargo-update already installed" 22 | - cargo install cargo-travis || echo "cargo-travis already installed" 23 | - cargo install-update -a 24 | - rust: stable 25 | before_script: 26 | - export PATH=$HOME/.cargo/bin:$PATH 27 | script: 28 | - eval cargo doc --verbose 29 | allow_failures: 30 | - rust: stable 31 | env: FEATURES='' 32 | before_script: 33 | - export PATH=$HOME/.cargo/bin:$PATH 34 | - rustup component add rustfmt-preview 35 | script: 36 | - eval cargo fmt -- --write-mode=diff 37 | 38 | addons: 39 | apt: 40 | packages: 41 | - libcurl4-openssl-dev 42 | - libelf-dev 43 | - libdw-dev 44 | - binutils-dev 45 | - cmake 46 | sources: 47 | - kalakris-cmake 48 | 49 | cache: 50 | directories: 51 | - /home/travis/.cargo 52 | 53 | before_cache: 54 | - rm -rf /home/travis/.cargo/registry 55 | 56 | script: 57 | - eval cargo build --verbose $FEATURES 58 | - eval cargo test --verbose $FEATURES 59 | 60 | after_success: | 61 | case "$TRAVIS_RUST_VERSION" in 62 | *) 63 | cargo coveralls --verbose 64 | ;; 65 | esac 66 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mut_guard" 3 | version = "0.1.0" 4 | authors = [ "Geoffroy Couprie " ] 5 | description = "Run a function after some data was mutably borrowed" 6 | license = "MIT" 7 | repository = "https://github.com/Geal/mutguard" 8 | readme = "README.md" 9 | documentation = "https://docs.rs/mut_guard" 10 | keywords = ["invariant", "contract-programming"] 11 | categories = ["memory-management"] 12 | 13 | include = [ 14 | "LICENSE", 15 | ".gitignore", 16 | ".travis.yml", 17 | "Cargo.toml", 18 | "src/*.rs" 19 | ] 20 | 21 | [dev-dependencies] 22 | serde = "^1.0" 23 | serde_derive = "^1.0" 24 | serde_json = "^1.0" 25 | 26 | [badges] 27 | travis-ci = { repository = "Geal/mutguard" } 28 | coveralls = { repository = "Geal/mutguard", branch = "master", service = "github" } 29 | maintenance = { status = "actively-developed" } 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Geoffroy Couprie 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MutGuard 2 | 3 | [![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) 4 | [![Build Status](https://travis-ci.org/Geal/mutguard.svg?branch=master)](https://travis-ci.org/Geal/mutguard) 5 | [![Coverage Status](https://coveralls.io/repos/Geal/mutguard/badge.svg?branch=master)](https://coveralls.io/r/Geal/mutguard?branch=master) 6 | [![Crates.io Version](https://img.shields.io/crates/v/mut_guard.svg)](https://crates.io/crates/mut_guard) 7 | [![Documentation](https://docs.rs/mut_guard/badge.svg)](https://docs.rs/mut_guard) 8 | 9 | this library allows you to call a function after 10 | some data has been mutably borrowed. 11 | 12 | *Note*: that function will be called from a `Drop` handler 13 | 14 | ## Use cases 15 | 16 | ### Invariant checks 17 | 18 | It can be used to enforce invariant: every time a `&mut` is obtained, 19 | be it from the element's method definition, or from external code 20 | accessing public members directly, the invariant check will run and 21 | verify the data is correct. 22 | 23 | ```rust 24 | extern crate mut_guard; 25 | use mut_guard::*; 26 | 27 | #[derive(Debug)] 28 | struct LessThan20(pub u8); 29 | 30 | impl Guard for LessThan20 { 31 | fn finish(&mut self) { 32 | assert!(self.0 <= 20, "invariant failed, internal value is too large: {}", self.0); 33 | } 34 | } 35 | 36 | fn main() { 37 | let mut val = MutGuard::new(LessThan20(0)); 38 | 39 | //"val: 0" 40 | println!("val: {:?}", *val); 41 | 42 | // this would fail because MutGuard does not implement DerefMut directly 43 | //val.0 = 10; 44 | 45 | // use the guard() method to get a `&mut LessThan20` 46 | val.guard().0 = 10; 47 | 48 | //"val: 10" 49 | println!("val: {:?}", *val); 50 | 51 | // once the value returned by guard() is dropped, the invariant will be checked 52 | // This code will panic with the following message: 53 | // 'invariant failed, internal value is too large: 30' 54 | val.guard().0 = 30; 55 | } 56 | ``` 57 | 58 | ### Logging 59 | 60 | Since the guard will be called every time there's a mutable access, we can log the changes 61 | there: 62 | 63 | ```rust 64 | # extern crate mut_guard; 65 | # use mut_guard::*; 66 | # 67 | # fn main() { 68 | let v = Vec::new(); 69 | 70 | // the wrap methods allows us to Specifiesy a closure instead of manually 71 | // implementing Guard 72 | let mut iv = MutGuard::wrap(v, |ref mut vec| { 73 | println!("vector content is now {:?}", vec); 74 | }); 75 | 76 | iv.guard().push(1); 77 | // prints "vector content is now [1]" 78 | 79 | iv.guard().push(2); 80 | // prints "vector content is now [1, 2]" 81 | 82 | iv.guard().push(3); 83 | // prints "vector content is now [1, 2, 3]" 84 | # } 85 | ``` 86 | 87 | ### Serialization 88 | 89 | The guard function could be used to store the element to a file after every change. 90 | 91 | ```rust 92 | #[macro_use] 93 | extern crate serde_derive; 94 | extern crate mut_guard; 95 | extern crate serde; 96 | extern crate serde_json; 97 | 98 | use mut_guard::*; 99 | use std::fs::File; 100 | 101 | #[derive(Serialize, Debug)] 102 | struct Data { 103 | pub a: u32, 104 | pub s: String, 105 | pub v: Vec, 106 | } 107 | 108 | impl Guard for Data { 109 | fn finish(&mut self) { 110 | File::create("data.json") 111 | .map_err(|e| { 112 | println!("could not create data file"); 113 | }) 114 | .and_then(|mut f| { 115 | serde_json::to_writer(f, self).map_err(|e| { 116 | println!("could not serialize data: {:?}", e); 117 | }) 118 | }); 119 | } 120 | } 121 | 122 | fn main() { 123 | let mut data = MutGuard::new(Data { 124 | a: 0, 125 | s: "hello".to_string(), 126 | v: vec![1, 2], 127 | }); 128 | 129 | data.guard().s = "Hello world".to_string(); 130 | // data.json was created and now contains: 131 | // {"a":0,"s":"Hello world","v":[1,2]} 132 | } 133 | ``` 134 | -------------------------------------------------------------------------------- /examples/serialization.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | extern crate mut_guard; 4 | extern crate serde; 5 | extern crate serde_json; 6 | 7 | use mut_guard::*; 8 | use std::fs::File; 9 | 10 | #[derive(Serialize, Debug)] 11 | struct Data { 12 | pub a: u32, 13 | pub s: String, 14 | pub v: Vec, 15 | } 16 | 17 | impl Guard for Data { 18 | fn finish(&mut self) { 19 | let _ = File::create("data.json") 20 | .map_err(|e| { 21 | println!("could not create data file: {:?}", e); 22 | }) 23 | .and_then(|f| { 24 | serde_json::to_writer(f, self).map_err(|e| { 25 | println!("could not serialize data: {:?}", e); 26 | }) 27 | }); 28 | } 29 | } 30 | 31 | fn main() { 32 | let mut data = MutGuard::new(Data { 33 | a: 0, 34 | s: "hello".to_string(), 35 | v: vec![1, 2], 36 | }); 37 | 38 | data.guard().s = "Hello world".to_string(); 39 | // data.json was created and now contains: 40 | // {"a":0,"s":"Hello world","v":[1,2]} 41 | } 42 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # MutGuard 2 | //! 3 | //! this library allows you to call a function after 4 | //! some data has been mutably borrowed. 5 | //! 6 | //! *Note*: that function will be called from a `Drop` handler 7 | //! 8 | //! ## Use cases 9 | //! 10 | //! ### Invariant checks 11 | //! 12 | //! It can be used to enforce invariant: every time a `&mut` is obtained, 13 | //! be it from the element's method definition, or from external code 14 | //! accessing public members directly, the invariant check will run and 15 | //! verify the data is correct. 16 | //! 17 | //! ```rust,should_panic 18 | //! extern crate mut_guard; 19 | //! use mut_guard::*; 20 | //! 21 | //! #[derive(Debug)] 22 | //! struct LessThan20(pub u8); 23 | //! 24 | //! impl Guard for LessThan20 { 25 | //! fn finish(&mut self) { 26 | //! assert!(self.0 <= 20, "invariant failed, internal value is too large: {}", self.0); 27 | //! } 28 | //! } 29 | //! 30 | //! fn main() { 31 | //! let mut val = MutGuard::new(LessThan20(0)); 32 | //! 33 | //! //"val: 0" 34 | //! println!("val: {:?}", *val); 35 | //! 36 | //! // this would fail because MutGuard does not implement DerefMut directly 37 | //! //val.0 = 10; 38 | //! 39 | //! // use the guard() method to get a `&mut LessThan20` 40 | //! val.guard().0 = 10; 41 | //! 42 | //! //"val: 10" 43 | //! println!("val: {:?}", *val); 44 | //! 45 | //! // once the value returned by guard() is dropped, the invariant will be checked 46 | //! // This code will panic with the following message: 47 | //! // 'invariant failed, internal value is too large: 30' 48 | //! val.guard().0 = 30; 49 | //! } 50 | //! ``` 51 | //! 52 | //! ### Logging 53 | //! 54 | //! Since the guard will be called every time there's a mutable access, we can log the changes 55 | //! there: 56 | //! 57 | //! ```rust 58 | //! # extern crate mut_guard; 59 | //! # use mut_guard::*; 60 | //! # 61 | //! # fn main() { 62 | //! let v = Vec::new(); 63 | //! 64 | //! // the wrap methods allows us to Specifiesy a closure instead of manually 65 | //! // implementing Guard 66 | //! let mut iv = MutGuard::wrap(v, |ref mut vec| { 67 | //! println!("vector content is now {:?}", vec); 68 | //! }); 69 | //! 70 | //! iv.guard().push(1); 71 | //! // prints "vector content is now [1]" 72 | //! 73 | //! iv.guard().push(2); 74 | //! // prints "vector content is now [1, 2]" 75 | //! 76 | //! iv.guard().push(3); 77 | //! // prints "vector content is now [1, 2, 3]" 78 | //! # } 79 | //! ``` 80 | //! 81 | //! ### Serialization 82 | //! 83 | //! The guard function could be used to store the element to a file after every change 84 | //! 85 | //! ```rust 86 | //! #[macro_use] 87 | //! extern crate serde_derive; 88 | //! extern crate mut_guard; 89 | //! extern crate serde; 90 | //! extern crate serde_json; 91 | //! 92 | //! use mut_guard::*; 93 | //! use std::fs::File; 94 | //! 95 | //! #[derive(Serialize, Debug)] 96 | //! struct Data { 97 | //! pub a: u32, 98 | //! pub s: String, 99 | //! pub v: Vec, 100 | //! } 101 | //! 102 | //! impl Guard for Data { 103 | //! fn finish(&mut self) { 104 | //! File::create("data.json") 105 | //! .map_err(|e| { 106 | //! println!("could not create data file"); 107 | //! }) 108 | //! .and_then(|mut f| { 109 | //! serde_json::to_writer(f, self).map_err(|e| { 110 | //! println!("could not serialize data: {:?}", e); 111 | //! }) 112 | //! }); 113 | //! } 114 | //! } 115 | //! 116 | //! fn main() { 117 | //! let mut data = MutGuard::new(Data { 118 | //! a: 0, 119 | //! s: "hello".to_string(), 120 | //! v: vec![1, 2], 121 | //! }); 122 | //! 123 | //! data.guard().s = "Hello world".to_string(); 124 | //! // data.json was created and now contains: 125 | //! // {"a":0,"s":"Hello world","v":[1,2]} 126 | //! } 127 | //! ``` 128 | //! 129 | use std::ops::{Deref, DerefMut, Drop}; 130 | 131 | /// stores an inner element that must implement the `Guard` trait, 132 | /// and forbids mutable borrows except going through its `guard()` method. 133 | pub struct MutGuard { 134 | inner: T, 135 | } 136 | 137 | impl Deref for MutGuard { 138 | type Target = T; 139 | 140 | fn deref(&self) -> &T { 141 | &self.inner 142 | } 143 | } 144 | 145 | /// Specifies a method that will be called after every time an element 146 | /// protected by a `Mutguard` will be mutably borrowed 147 | pub trait Guard { 148 | fn finish(&mut self); 149 | } 150 | 151 | impl MutGuard { 152 | pub fn new(inner: T) -> MutGuard { 153 | MutGuard { inner } 154 | } 155 | 156 | /// call this method to get mutable access to the underlying element 157 | pub fn guard(&mut self) -> MutGuardBorrow { 158 | MutGuardBorrow { inner: self } 159 | } 160 | 161 | /// returns the wrapped element, consuming the MutGuard 162 | pub fn into_inner(self) -> T { 163 | self.inner 164 | } 165 | } 166 | 167 | impl<'a, T> MutGuard> { 168 | /// This method automatically generates a `Guard` implementation that will 169 | /// call `f` after every time the inner element is mutably borrowed 170 | pub fn wrap(inner: T, f: F) -> MutGuard> 171 | where 172 | F: 'a + for<'r> FnMut(&'r mut T), 173 | { 174 | let wrapper = MutGuardWrapper { 175 | inner, 176 | f: Box::new(f), 177 | }; 178 | MutGuard::new(wrapper) 179 | } 180 | } 181 | 182 | /// Structure returned by the `MutGuard::guard()`. when this is dropped, it 183 | /// will call the `Guard::finish()` method of the wrapped element 184 | pub struct MutGuardBorrow<'a, T: 'a + Guard> { 185 | inner: &'a mut MutGuard, 186 | } 187 | 188 | impl<'a, T: Guard> Deref for MutGuardBorrow<'a, T> { 189 | type Target = T; 190 | 191 | fn deref(&self) -> &T { 192 | &self.inner.inner 193 | } 194 | } 195 | 196 | impl<'a, T: Guard> DerefMut for MutGuardBorrow<'a, T> { 197 | fn deref_mut(&mut self) -> &mut T { 198 | &mut self.inner.inner 199 | } 200 | } 201 | 202 | impl<'a, T: Guard> Drop for MutGuardBorrow<'a, T> { 203 | fn drop(&mut self) { 204 | self.inner.inner.finish(); 205 | } 206 | } 207 | 208 | /// `Guard` implementation returned by `MutGuard::wrap()` 209 | pub struct MutGuardWrapper<'a, T> { 210 | inner: T, 211 | f: Box<'a + FnMut(&mut T)>, 212 | } 213 | 214 | impl<'a, T: 'a> MutGuardWrapper<'a, T> { 215 | pub fn new(inner: T, f: F) -> MutGuardWrapper<'a, T> 216 | where 217 | F: 'a + for<'r> FnMut(&'r mut T), 218 | { 219 | MutGuardWrapper { 220 | inner, 221 | f: Box::new(f), 222 | } 223 | } 224 | } 225 | 226 | impl<'a, T> Guard for MutGuardWrapper<'a, T> { 227 | fn finish(&mut self) { 228 | (self.f)(&mut self.inner); 229 | } 230 | } 231 | 232 | impl<'a, T> Deref for MutGuardWrapper<'a, T> { 233 | type Target = T; 234 | 235 | fn deref(&self) -> &T { 236 | &self.inner 237 | } 238 | } 239 | 240 | impl<'a, T> DerefMut for MutGuardWrapper<'a, T> { 241 | fn deref_mut(&mut self) -> &mut T { 242 | &mut self.inner 243 | } 244 | } 245 | 246 | #[cfg(test)] 247 | mod tests { 248 | use super::*; 249 | 250 | #[derive(Debug)] 251 | struct Bank { 252 | accounts: Vec, 253 | } 254 | 255 | impl Bank { 256 | pub fn new(accounts: Vec) -> Bank { 257 | Bank { accounts } 258 | } 259 | 260 | pub fn transfer(&mut self, account1: usize, account2: usize, amount: i32) { 261 | self.accounts[account1] -= amount; 262 | self.accounts[account2] += amount; 263 | } 264 | } 265 | 266 | impl Guard for Bank { 267 | fn finish(&mut self) { 268 | assert!( 269 | self.accounts.iter().any(|v| *v < 0), 270 | "accounts should not become negative" 271 | ); 272 | } 273 | } 274 | 275 | #[test] 276 | #[should_panic(expected = "accounts should not become negative")] 277 | fn invariant_bank() { 278 | let mut ibank = MutGuard::new(Bank::new(vec![10, 0, 20, 50])); 279 | 280 | println!("bank: {:?}", *ibank); 281 | 282 | { 283 | ibank.guard().transfer(0, 1, 5); 284 | println!("bank: {:?}", *ibank); 285 | 286 | ibank.guard().transfer(2, 3, 30); 287 | println!("bank: {:?}", *ibank); 288 | } 289 | } 290 | 291 | #[test] 292 | fn bank() { 293 | let mut bank = Bank::new(vec![10, 0, 20, 50]); 294 | println!("bank: {:?}", bank); 295 | 296 | bank.transfer(0, 1, 5); 297 | println!("bank: {:?}", bank); 298 | 299 | bank.transfer(2, 3, 30); 300 | // now accounts are [5, 5, -10, 80] 301 | println!("bank: {:?}", bank); 302 | } 303 | 304 | #[test] 305 | fn count_access() { 306 | let mut counter = 0; 307 | let v = Vec::new(); 308 | 309 | { 310 | let mut iv = MutGuard::wrap(v, |_| counter += 1); 311 | 312 | iv.guard().push(1); 313 | iv.guard().push(2); 314 | iv.guard().push(3); 315 | assert_eq!(iv[0], 1); 316 | assert_eq!(iv[1], 2); 317 | assert_eq!(iv[2], 3); 318 | } 319 | 320 | assert_eq!(counter, 3); 321 | } 322 | 323 | #[test] 324 | #[should_panic] 325 | fn less_than() { 326 | #[derive(Debug)] 327 | struct LessThan20(pub u8); 328 | 329 | impl Guard for LessThan20 { 330 | fn finish(&mut self) { 331 | assert!( 332 | self.0 <= 20, 333 | "invariant failed, internal value is too large: {}", 334 | self.0 335 | ); 336 | } 337 | } 338 | 339 | let mut val = MutGuard::new(LessThan20(0)); 340 | 341 | //"val: 0" 342 | println!("val: {:?}", *val); 343 | 344 | // this would fail because MutGuard does not implement DerefMut directly 345 | //val.0 = 10; 346 | 347 | // use the guard() method to get a `&mut LessThan20` 348 | val.guard().0 = 10; 349 | 350 | //"val: 10" 351 | println!("val: {:?}", *val); 352 | 353 | // once the value returned by guard() is dropped, the invariant will be checked 354 | // we get the message "panicked at 'invariant failed, internal value is too large: 30'" 355 | val.guard().0 = 30; 356 | } 357 | 358 | #[test] 359 | #[should_panic(expected = "other panic")] 360 | fn other_panic() { 361 | #[derive(Debug)] 362 | struct LessThan20(pub u8); 363 | 364 | impl Guard for LessThan20 { 365 | fn finish(&mut self) { 366 | assert!( 367 | self.0 <= 20, 368 | "invariant failed, internal value is too large: {}", 369 | self.0 370 | ); 371 | } 372 | } 373 | 374 | let mut val = MutGuard::new(LessThan20(0)); 375 | 376 | let v = val.guard(); 377 | panic!("other panic"); 378 | } 379 | 380 | #[test] 381 | fn mem_forget() { 382 | use std::mem; 383 | 384 | let mut counter = 0; 385 | let v = Vec::new(); 386 | 387 | { 388 | let mut iv = MutGuard::wrap(v, |_| counter += 1); 389 | 390 | { 391 | let mut g = iv.guard(); 392 | g.push(1); 393 | mem::forget(g); 394 | } 395 | 396 | iv.guard().push(2); 397 | iv.guard().push(3); 398 | assert_eq!(iv[0], 1); 399 | assert_eq!(iv[1], 2); 400 | assert_eq!(iv[2], 3); 401 | } 402 | 403 | // with mem::forget, drop() will not be called on the guard 404 | assert_eq!(counter, 2); 405 | } 406 | } 407 | --------------------------------------------------------------------------------