Nested Closure. How can I have following codes compiled?

Thanks in advance.

struct Sender<T> {
  send: Box<dyn FnMut(T)>,
}
impl<T> Sender<T> {
  pub fn new(send: Box<dyn FnMut(T)>) -> Self {
    Sender { send }
  }
}

fn main() {
  let a = "1".to_owned();
  let b = "2".to_owned();
  let c = "3".to_owned();
  let mut sender1 = Sender::<String>::new(Box::new(|x| {
    let mut sender2 = Sender::<String>::new(Box::new(|y| {
      let mut sender3 = Sender::<String>::new(Box::new(|z| {
        println!("{:?} {:?} {:?}", x, y, z);
      }));
      (sender3.send)(c);
    }));
    (sender2.send)(b)
  }));

  (sender1.send)(a);
}

This is not possible without more code to clone the strings.

Specifically, think about what happens if I run this:

let sender2 = (sender1.send)("1".to_owned());
let sender3_a = (sender2.send("2a".to_owned());
let sender3_b = (sender2.send("2b".to_owned());

At this point, both sender3_a and sender3_b need access to x which is "1".to_owned(). But since neither is borrowing from the other (they both exist completely independently), they both need to own x. Only one thing can own a variable at a time, so this can't work - you need to clone the arguments at some point, or do something else to share them.

This is also a problem for c and b - you are taking ownership when you pass them into sender3.send and sender2.send on lines 20 and 22. You effectively need to be able to pass c into sender3.send any number of times, but you've only created one String. This can also be fixed by cloning things as you pass them around, if you're OK with that (and that works with your original use case, if it isn't Strings).

Lastly, if you do this, you'll be effectively borrowing all the variables rather than moving them. Rust will automatically turn this into having each closure only take borrows (for instance, then |y| closure will only borrow x, not move it in). We want the closures to all be independent, so they do need to move their captured variables in rather than making new borrows. The move keyword does this.

In total, this looks like:

struct Sender<T> {
  send: Box<dyn FnMut(T)>,
}
impl<T> Sender<T> {
  pub fn new(send: Box<dyn FnMut(T)>) -> Self {
    Sender { send }
  }
}

fn main() {
  let a = "1".to_owned();
  let b = "2".to_owned();
  let c = "3".to_owned();
  let mut sender1 = Sender::<String>::new(Box::new(move |x| {
    let c_clone = c.clone();
    let mut sender2 = Sender::<String>::new(Box::new(move |y| {
      let x_clone = x.clone();
      let mut sender3 = Sender::<String>::new(Box::new(move |z| {
        println!("{:?} {:?} {:?}", x_clone, y, z);
      }));
      (sender3.send)(c_clone.clone());
    }));
    (sender2.send)(b.clone());
  }));

  (sender1.send)(a);
}

You'll need more clones at each additional layer, as well. Here's an example with 4 layers. I've left out the _clone suffix on the new variables to make it read better (but it's doing the exact same thing as if you had a _clone suffix):

struct Sender<T> {
  send: Box<dyn FnMut(T)>,
}
impl<T> Sender<T> {
  pub fn new(send: Box<dyn FnMut(T)>) -> Self {
    Sender { send }
  }
}

fn main() {
  let a = "1".to_owned();
  let b = "2".to_owned();
  let c = "3".to_owned();
  let d = "4".to_owned();
  let mut sender1 = Sender::<String>::new(Box::new(move |x| {
    let c = c.clone();
    let d = d.clone();
    let mut sender2 = Sender::<String>::new(Box::new(move |y| {
      let x = x.clone();
      let d = d.clone();
      let mut sender3 = Sender::<String>::new(Box::new(move |z| {
        let x = x.clone();
        let y = y.clone();
        let mut sender4 = Sender::<String>::new(Box::new(move |w| {
            println!("{:?} {:?} {:?} {:?}", x, y, z, w);
        }));
        (sender4.send)(d.clone());
      }));
      (sender3.send)(c.clone());
    }));
    (sender2.send)(b.clone());
  }));

  (sender1.send)(a);
}

If your actual application doesn't allow cloneing the things returned, an alternative is wrapping everything in Arcs. Arc clones are cheap, and if you wrap your things in Arcs, multiple closures can then effectively own the underlying data (at the cost of only having borrowed access to it).

Or, if you're OK with each closure only being callable once, you could use FnOnce() rather than FnMut(). This would fix the issue by telling rust that each closure only needs to be run once, so it's 100% fine that they move ownership of the variables around and can only be run once. (You might still need move on some of the closures to make this compile, but that should also be fine).

Let me know if any of these work for you and your use case!

1 Like

@daboross Thank you so much. What a clear explanation! I'v been thinking that's a lifetime problem, and have tried everything but clone. it's good to know Arc's clones are cheap.

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.