In Rust, can I expose an array of functions from my module?

Viewed 297

I'm implementing a game engine, and want to have a set of modules, each of which implements certain rules. Each module exposes a function which conforms to this definition:

fn validate(game: &Game, next_move: String) -> Result<(), MoveError>

Rather than have consumers of these rules use every rule module, I'd like to expose a set (array? Vec<>?) of these validate functions. I was assuming it would be an array of function pointers, something like:

mod rule1;
mod rule2;

type Validate = fn(&Game, String) -> Result<(), MoveError>;

const validations: [Validate] = &[
    rule1::validate,
    rule2::validate
];

but Rust says:

error[E0277]: the size for values of type `[for<'r> fn(&'r Game, std::string::String) -> std::result::Result<(), MoveError>]` cannot be known at compilation time
 --> src/lib.rs:6:20
  |
6 | const validations: [Validate] = &[
  |                    ^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `[for<'r> fn(&'r Game, std::string::String) -> std::result::Result<(), MoveError>]`
  = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>

I've read the linked article and am none the wiser. What's the first step in fixing this error (or perhaps in correcting my lack of understanding)

2 Answers

Your problem has nothing to do with functions.

You can reproduce it with the following:

const validation: [u32] = &[ 42 ];

which gives

error[E0277]: the size for values of type `[u32]` cannot be known at compilation time
 --> src/lib.rs:1:19
  |
1 | const validation: [u32] = &[ 42 ];
  |                   ^^^^^ doesn't have a size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `[u32]`
  = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>

The issue is that [T] is not a Sized type, no matter what T is. This means you cannot use it in const.

You can however use a reference:

const validation: &[u32] = &[ 42 ];

or a sized array:

const validation: [u32; 1] = [ 42 ];

Same applies to your original problem:


const validations: &[Validate] = &[
//                 ^
    rule1::validate,
    rule2::validate
];
struct Game;
struct MoveError;

mod rule1 {
    use super::*;
    pub(crate) fn validate(game: &Game, s: String) -> Result<(), MoveError> {
        unimplemented!()
    }
}

mod rule2 {
    use super::*;
    pub(crate) fn validate(game: &Game, s: String) -> Result<(), MoveError> {
        unimplemented!()
    }
}

const VALIDATIONS: &[fn(&Game, String) -> Result<(), MoveError>] = &[
    rule1::validate,
    rule2::validate,
];

// or...

use lazy_static::lazy_static;

lazy_static! {
    static ref VALIDATIONS_2: &[Box<dyn Fn(&Game, String) -> Result<(), MoveError>>] = &[
        Box::new(rule1::validate),
        Box::new(rule2::validate)
    ];    
}
Related