I'm a newbie with Rust and I bumped into some obstacles when dealing with closures, either when returning them from functions and or methods, either when I need to store them as struct fields.
Let's start with what is working:
fn build_func(b: usize) -> impl Fn(usize) -> usize {
move |a| a + b
}
struct Foo<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
impl<F> Foo<F>
where
F: Fn(usize) -> usize,
{
fn new(foo: F) -> Self {
Self { foo }
}
}
fn main() {
let foo1 = Foo { foo: |a| a + 1 };
let foo2 = Foo { foo: build_func(2) };
let foo_func = build_func(3);
let foo3 = Foo { foo: foo_func };
}
This works as expected and the type of the closure that is built outside the struct is properly matched with the generic of Foo
.
I wanted to achieve the same, but by hiding the creation of the closure simply by moving it inside the impl
of Foo
itself.
I tested these alternatives, but none of theme compiles:
struct Foo2<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
impl<F> Foo2<F>
where
F: Fn(usize) -> usize,
{
fn new() -> Self {
let foo = build_func(1);
Self { foo }
}
}
struct Foo3<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
impl<F> Foo3<F>
where
F: Fn(usize) -> usize,
{
fn new() -> Self {
let foo = |a| a + 1;
Self { foo }
}
}
struct Foo4<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
impl<F> Foo4<F>
where
F: Fn(usize) -> usize,
{
fn new() -> Self {
let foo = Self::build_func(1);
Self { foo }
}
fn build_func(b: usize) -> F {
move |a| a + b
}
}
struct Foo5<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
impl<F> Foo5<F>
where
F: Fn(usize) -> usize,
{
fn new() -> Self {
let foo = Self::build_func(1);
Self { foo }
}
fn build_func(b: usize) -> impl Fn(usize) -> usize {
move |a| a + b
}
}
I understand that each closure has its own opaque and distinct type, but then I don't understand why on the other hand the initial implementation of Foo
works then.
By reading the accepted answer here I seem to understand that the only option, in this case, would be to box the closure, but I still don't have a full understanding.
By combining boxed closure and trait aliases (I know it's not the "real" trait aliasing) I came up with this:
trait Func: Fn(usize) -> usize {}
impl<T> Func for T where T: Fn(usize) -> usize {}
struct Foo6 {
pub foo: Box<dyn Func>,
}
impl Foo6 {
fn new() -> Self {
let foo = Self::build_func(1);
Self { foo: Box::new(foo) }
}
fn build_func(b: usize) -> impl Func {
move |a| a + b
}
}
fn main() {
let foo = Foo6::new();
println!("{}", (foo.foo)(1));
}
But I'm wondering whether it's possible to obtain an unboxed version.