1
struct User {
    id: i8,
    name: &'static str
}

struct UserMethods {
    add_user: Fn(User) -> (),
}

fn main() {
    UserMethods {
        add_user: |user| {

        }
    };
}

The compiler says,

error[E0277]: the size for values of type `(dyn Fn(User) + 'static)` cannot be known at compilation time
  --> src/main.rs:11:5
   |
11 | /     UserMethods {
12 | |         add_user: |user| {},
13 | |     };
   | |_____^ doesn't have a size known at compile-time
Svetlin Zarev
  • 14,713
  • 4
  • 53
  • 82
Axel
  • 4,365
  • 11
  • 63
  • 122

2 Answers2

6

Fn is a trait, thus it is unsized. In order to store it in a struct or variable, you must use something that implements Sized otherwise the compiler cannot know how much memory to allocate.

There are two ways to fix the issue:

  1. Use generics:
struct UserMethods<T: Fn(User)> {
    add_user: T,
}

fn main() {
    UserMethods {
        add_user: |user| {},
    };
}
  1. Use Box or any other smart pointer:
struct UserMethods {
    add_user: Box<dyn Fn(User)>,
}

fn main() {
    UserMethods {
        add_user: Box::new(|user| {}),
    };
}

You can also use an ordinary function pointer (thanks @user2722968), but it's not as flexible as a closure (cannot capture anything from the environment):

struct UserMethods {
    add_user: fn(User),
}

fn main() {
    UserMethods {
        add_user: |user| {},
    };
}
Svetlin Zarev
  • 14,713
  • 4
  • 53
  • 82
  • Thanks a lot for the answer. How is `fn` different from `Fn`. Can you please explain? I read few articles but I can't seem to wrap my head around it. – Axel Jun 21 '21 at 11:19
  • 2
    The third possibility (which is implicit in the `generic`-one) is to use simple function pointers. That is, `fn(User)` instead of `Fn(User)`; `fn` is `Sized`, because free-standing functions can't capture their environment. – user2722968 Jun 21 '21 at 11:20
  • `fn` is an ordinary function pointer, while `Fn` is a trait: https://stackoverflow.com/a/36390748/2588800 – Svetlin Zarev Jun 21 '21 at 11:22
0

SvetlinZarev's second option only works if your closure is 'static (it implies dyn Fn(User) + 'static), if it's not you have to explicitly name the lifetimes, something like this:

struct UserMethods<'a> {
    add_user: Box<dyn Fn(User) + 'a>,
}

fn main() {
    let a = 99;
    UserMethods {
        add_user: Box::new(|user| { println!("{a}") }),
    };
}
cafce25
  • 15,907
  • 4
  • 25
  • 31