Last active
December 1, 2018 00:21
-
-
Save alphaville/59729d099899f44e351dd0e09ca31aca to your computer and use it in GitHub Desktop.
Using closures in structures (Rust)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // ---------------------------------------------------------------------------- | |
| // Boxed simple function | |
| // ---------------------------------------------------------------------------- | |
| struct Chung<'a> { | |
| close: Box<Fn(f64) -> f64 + 'a>, | |
| } | |
| impl<'a> Chung<'a> { | |
| fn new<C>(f: C) -> Chung<'a> | |
| where | |
| C: Fn(f64) -> f64 + 'a, | |
| { | |
| Chung { close: Box::new(f) } | |
| } | |
| fn apply(&self, x: f64) -> f64 { | |
| (self.close)(x) | |
| } | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // Boxed function with references | |
| // ---------------------------------------------------------------------------- | |
| struct Robot<'a> { | |
| // Note: We have to write Box<Fn(&[f64]) -> f64 + 'a>; not just Box<F> and | |
| // define `F` separately through a `where` statement. Secondly, we must | |
| // specify a lifetime for the data type. If we don't specify a lifetime, | |
| // we'll get an error. An alternative possibility is to define type `C`, | |
| // below, as 'static, however, this approach is better because we don't | |
| // force the type to live throughout the whole program. | |
| fun: Box<Fn(&[f64]) -> f64 + 'a>, | |
| } | |
| impl<'a> Robot<'a> { | |
| fn new<C>(f: C) -> Robot<'a> | |
| where | |
| C: Fn(&[f64]) -> f64 + 'a, | |
| { | |
| Robot { fun: Box::new(f) } | |
| } | |
| fn doit(&self, x: &[f64]) -> f64 { | |
| (self.fun)(x) | |
| } | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // Modifier: struct which stores a boxed closure which takes in a mutable | |
| // slice and can modify it | |
| // ---------------------------------------------------------------------------- | |
| struct Modifier<'a> { | |
| worker: Box<Fn(&mut [f64]) + 'a>, | |
| } | |
| impl<'a> Modifier<'a> { | |
| fn new<C>(f: C) -> Modifier<'a> | |
| where | |
| C: Fn(&mut [f64]) + 'a, | |
| { | |
| Modifier { | |
| worker: Box::new(f), | |
| } | |
| } | |
| fn modify(&self, x: &mut [f64]) { | |
| (self.worker)(x); | |
| (self.worker)(x); | |
| } | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // Submarine: now a closure is provided which modifies the internal state of | |
| // the structure + the structure has more fields | |
| // ---------------------------------------------------------------------------- | |
| struct Submarine<'a> { | |
| state: &'a mut [f64], | |
| worker: Box<Fn(&mut [f64]) + 'a>, | |
| } | |
| impl<'a> Submarine<'a> { | |
| fn new<C>(f: C, x: &'a mut [f64]) -> Submarine<'a> | |
| where | |
| C: Fn(&mut [f64]) + 'a, | |
| { | |
| Submarine { | |
| worker: Box::new(f), | |
| state: x, | |
| } | |
| } | |
| fn do_it(&mut self) { | |
| (self.worker)(self.state); | |
| (self.worker)(self.state); | |
| } | |
| fn get_state(&self) -> &[f64] { | |
| self.state | |
| } | |
| fn get_worker(&self) -> &Box<Fn(&mut [f64]) + 'a> { | |
| &self.worker | |
| } | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // Simple function without a box | |
| // ---------------------------------------------------------------------------- | |
| struct ChungUnboxed<F> | |
| where F : Fn(f64) -> f64 , { | |
| close: F, | |
| } | |
| impl<F> ChungUnboxed<F> | |
| where F : Fn(f64) -> f64, | |
| { | |
| fn new(f: F) -> ChungUnboxed<F> | |
| { | |
| ChungUnboxed { close: f } | |
| } | |
| fn apply(&self, x: f64) -> f64 { | |
| (self.close)(x) | |
| } | |
| } | |
| // ---------------------------------------------------------------------------- | |
| // Just a function invocator | |
| // ---------------------------------------------------------------------------- | |
| fn invoke<F>(f: Box<F>, x: f64) -> f64 | |
| where | |
| F: Fn(f64) -> f64, | |
| { | |
| f(x) | |
| } | |
| fn main() { | |
| } | |
| #[cfg(test)] | |
| mod tests { | |
| use super::*; | |
| #[test] | |
| fn test_invole() { | |
| let fun = |x| x + 1.0_f64; | |
| let boxf = Box::new(fun); | |
| let r = invoke(boxf, 2.4); | |
| assert_eq!(r, 3.4); | |
| } | |
| #[test] | |
| fn test_chung() { | |
| let fun = |x: f64| x + 1.0_f64; | |
| let ch = Chung::new(fun); | |
| let r = ch.apply(4.5); | |
| assert_eq!(r, 5.5); | |
| } | |
| #[test] | |
| fn test_robot() { | |
| let f = |x: &[f64]| -> f64 { x.iter().sum() }; | |
| let robot = Robot::new(f); | |
| let result = robot.doit(&[1.0, 2.0, 5.0, 7.0]); | |
| assert_eq!(result, 15.0); | |
| } | |
| #[test] | |
| fn test_modifier() { | |
| let g = |x: &mut [f64]| x.iter_mut().for_each(|s| *s += 1.0); | |
| let modifier = Modifier::new(g); | |
| let mut arr = [1.0, 2.0]; | |
| modifier.modify(&mut arr); | |
| assert_eq!(arr, [3.0, 4.0]); | |
| } | |
| #[test] | |
| fn test_submarine() { | |
| let mut arr = [1.0, 2.0, 9.0, -3.0]; | |
| let g = |x: &mut [f64]| x.iter_mut().for_each(|s| *s += 100.0); | |
| let mut sbm = Submarine::new(g, &mut arr); | |
| sbm.do_it(); | |
| let state = sbm.get_state(); | |
| assert_eq!([201.0, 202.0, 209.0, 197.0], state); | |
| let mut y = [9.0, 10.0, 2.0]; | |
| let worker = sbm.get_worker(); | |
| worker(&mut y); | |
| assert_eq!([109.0, 110.0, 102.0], y); | |
| } | |
| #[test] | |
| fn test_simple_no_box() { | |
| let fun = |x| x + 1.0_f64; | |
| let unboxed = ChungUnboxed::new(fun); | |
| let result = unboxed.apply(7.0_f64); | |
| assert_eq!(8.0, result); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment