In Rust, every closure has its own unique type, which is different from the type of any other closure in the program, even if they have the same signature. When the closure does not capture any environment, this unique type can be automatically converted into the function pointer type, which is the kind of type used for Doer
, but your closure does capture the environment (specifically, the v
variable).
One thing you can do is the following:
struct State {
value: i64,
}
fn generate_doer(v: i64) -> impl Fn(State) -> Option<String> {
move |s: State| -> Option<String> {
if v == s.value {
return Some(String::from("All Good"));
} else {
return None;
}
}
}
Here, the impl Trait
syntax is a way to say "this function returns some type that implements this trait" without saying what the type actually is. In this case, the compiler would replace it with the anonymous type that the closure is given at compile time.
However since impl Trait
is not a specific type, but a placeholder that is replaced with one of many types at compile time, you can't use it outside of function signatures. Struct fields require an actual specific type.
If you need an actual specific type, you can instead do this:
struct State {
value: i64,
}
type Doer = Box<dyn Fn(State) -> Option<String>>;
fn generate_doer(v: i64) -> Doer {
Box::new(move |s: State| -> Option<String> {
if v == s.value {
return Some(String::from("All Good"));
} else {
return None;
}
})
}
An dyn Trait
is a specific type called a trait object that any implementer of the trait can be converted into. So here, we first create a value of the closure's anonymous type, then box it, and then convert the box into the nameable type using dyn Trait
.
It is worth mentioning that what you are doing in Elm is equivalent to the Box
solution here. Elm has no equivalent of impl Trait
.