Skip to content

Instantly share code, notes, and snippets.

@alphaville
Last active December 1, 2018 00:21
Show Gist options
  • Select an option

  • Save alphaville/59729d099899f44e351dd0e09ca31aca to your computer and use it in GitHub Desktop.

Select an option

Save alphaville/59729d099899f44e351dd0e09ca31aca to your computer and use it in GitHub Desktop.
Using closures in structures (Rust)
// ----------------------------------------------------------------------------
// 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