How to decorate closures?

I need to distinguish how to print items of a vec based on a runtime parameter and some other runtime information. I am struggling a bit to make this work with boxed closures.

This seems to be the decorator pattern, but I can't seem to make it work. Please note that the function write_vec needs to be re-used and can contain other runtime information, which is why I need it declared . The problem is how to move a boxed closure to inside a closure without making the function FnOnce. Alternatively, how to pass a reference to the closure without leaking f

use std::fmt::{Debug, Formatter, Result, Write};

enum Type {
    Date,
    Int,
}

struct TypedVec {
    data: Vec<i32>,
    type_: Type,
}

fn get_write_value<'a, F: Write>(
    vec: &'a TypedVec,
) -> Box<dyn Fn(&mut F, usize) -> Result + 'a> {
    match vec.type_ {
        Type::Int => Box::new(|f, index| write!(f, "{}", vec.data[index])),
        Type::Date => Box::new(|f, index| write!(f, "{}-{}-{}", vec.data[index] / 365, (vec.data[index] % 365) / 30, vec.data[index] % 30)), // this is wrong, it does not matter
    }
}

fn write_vec<D, F>(
    f: &mut F,
    d: D,
    index: usize,
    len: usize,
) -> Result
where
    D: Fn(&mut F, usize) -> Result,
    F: Write,
{
    if index == 0 {
        f.write_char('[')?;
    }
    if index != 0 {
        f.write_char(',')?;
        f.write_char('\n')?;
    }
    d(f, index)?;
    if index + 1 == len {
        f.write_char(']')?;
    }
    Ok(())
}

impl Debug for TypedVec {
    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        let writer = get_write_value(self);

        let write = |f: &mut Formatter<'_>, index| write_vec(f, writer, index, self.data.len());

        for i in 0..self.data.len() {
            write(f, i)?;
        }
        Ok(())
    }
}


fn main() {
    
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0521]: borrowed data escapes outside of closure
  --> src/main.rs:50:52
   |
48 |         let writer = get_write_value(self);
   |             ------ `writer` declared here, outside of the closure body
49 | 
50 |         let write = |f: &mut Formatter<'_>, index| write_vec(f, writer, index, self.data.len());
   |                      -                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `f` escapes the closure body here
   |                      |
   |                      `f` is a reference that is only valid in the closure body
   |
   = note: requirement occurs because of a mutable reference to Formatter<'_>
   = note: mutable references are invariant over their type parameter
   = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance

error[E0382]: use of moved value: `write`
  --> src/main.rs:53:13
   |
53 |             write(f, i)?;
   |             ^^^^^------
   |             |
   |             `write` moved due to this call, in previous iteration of loop
   |
note: closure cannot be invoked more than once because it moves the variable `writer` out of its environment
  --> src/main.rs:50:65
   |
50 |         let write = |f: &mut Formatter<'_>, index| write_vec(f, writer, index, self.data.len());
   |                                                                 ^^^^^^
note: this value implements `FnOnce`, which causes it to be moved when called
  --> src/main.rs:53:13
   |
53 |             write(f, i)?;
   |             ^^^^^

Some errors have detailed explanations: E0382, E0521.
For more information about an error, try `rustc --explain E0382`.
error: could not compile `playground` due to 2 previous errors

Does this work for you?

    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
        let writer = get_write_value(self);
        let mut write = |index| write_vec(f, &*writer, index, self.data.len());

        for i in 0..self.data.len() {
            write(i)?;
        }
        Ok(())
    }

Playground.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.