Jan Špaček
Jan Špaček

Reputation: 1108

Is there any way to have boxed and by-move closures?

I need a closure that captures by-value and is called at most once, but I cannot have the function using the closure monomorphise on every passed closure, because the closures and functions are mutually recursive and the monomorphisation phase fails. I tried something like:

fn closure_user(closure: Box<FnOnce(usize) -> bool>) -> bool {
    closure(3)
}

fn main() {
    let big_data = vec![1, 2, 3, 4];
    closure_user(Box::new(|x| {
        let _ = big_data.into_iter();
        false
    }));
}
error[E0161]: cannot move a value of type dyn std::ops::FnOnce(usize) -> bool: the size of dyn std::ops::FnOnce(usize) -> bool cannot be statically determined
 --> src/main.rs:2:5
  |
2 |     closure(3)
  |     ^^^^^^^

The unboxed version is:

fn closure_user<F>(closure: F) -> bool
where
    F: FnOnce(usize) -> bool,
{
    closure(42)
}

fn main() {
    let big_data = vec![1, 2, 3, 4];
    closure_user(|x| {
        let _ = big_data.into_iter();
        false
    });
}

It seems that it is impossible to box and unbox the closure as a FnOnce trait object. Is there any way to have boxed (no type parameter) and by-move (one call only) closures?

Upvotes: 1

Views: 769

Answers (2)

Renato Zannon
Renato Zannon

Reputation: 29981

It is possible, but for now you have to do it through the unstable std::thunk::Thunk:

use std::thunk::{Invoke, Thunk};

fn closure_user(closure: Thunk<usize, bool>) -> bool {
    closure.invoke(3)
}

fn main() {
    let big_data = vec![1, 2, 3, 4];
    closure_user(Thunk::with_arg(|x| {
        let _ = big_data.into_iter();
        false
    }));
}

This is due to limitations on the current type system - it's not possible to move out from a trait object - and should be addressed soon. For more information, see the blog post Purging Proc.

Upvotes: 2

Shepmaster
Shepmaster

Reputation: 431789

As of Rust 1.35, this is now possible using your original syntax:

fn closure_user(closure: Box<dyn FnOnce(usize) -> bool>) -> bool {
    closure(3)
}

fn main() {
    let big_data = vec![1, 2, 3, 4];
    closure_user(Box::new(|x| {
        let _ = big_data.into_iter();
        false
    }));
}

Upvotes: 3

Related Questions