What does it mean when a type parameter is bounded by an Fn
type?
fn call<F: Fn() -> u64>(f: F) -> u64 {
f()
}
The Rust Book says that functions with type parameters are monomorphized: https://doc.rust-lang.org/stable/book/ch10-01-syntax.html?highlight=generic%20function#performance-of-code-using-generics.
If that's the case, then I'd expect a new version of call
to be generated for each new closure type it is instantiated with.
According to the Rust Reference, each closure type is distinct, even if the function body is the same: https://doc.rust-lang.org/reference/types/closure.html.
So when I look at the compiled output for this program, I'd expect the compilation artifact to be larger when I call call
with 1799999999999999999
distinct instantiations of F
as compared to just four instantiations, but that's not what happens.
(1)
// `call` is defined above
fn make_closure(n: u64) -> impl Fn() -> u64 {
move || n
}
fn main() {
let results = (0..17999999999999999999).map(make_closure).map(call);
for r in results {
println!("{}", r)
}
}
So what's the right mental model for what fn call<F: Fn() -> u64>
means?
Should I think of the lack of code bloat as merely an optimization?
It gets harder for me to sustain this mental model when the closure is built from user input, though:
(2)
fn main() {
let mut buf = String::new();
std::io::stdin().read_line(&mut buf).unwrap();
let n = buf.trim().parse::<u64>().unwrap();
println!("{}", call(make_closure(n)));
}
So what's the right way to think about what the signature of call
means?
Update
Adding more information so we can reference it from comments:
(3)
The Rust Reference says:
A closure expression produces a closure value with a unique, anonymous type that cannot be written out. A closure type is approximately equivalent to a struct which contains the captured variables.
(4) The first line below is accepted by rustc, the second is not:
vec![make_closure(1), make_closure(2)]; // OK
vec![(|| 1), (|| 1)]; // Error: mismatched types