To expand on this, consider the expression:
// global/compile-time type definition
// vvvvvvvvvvvvvvvvv
{ pub struct Foo(); Foo() }
// ^^^^^
// runtime instantiation of that type
-
Or, similarly, the expression:
{
fn foo() {} // <- compile-time: "global" function definition (a constant and a hidden type; let us call its type `Foo`)
foo // <- runtime: refer to this very function to "instantiate" it
}
Each time the expression is written, a new unique Foo
type is defined, and you get an instance of that type.
So you can't do:
let mut vec = Vec::new();
vec.push({ struct Foo(); Foo() }); // l2:
vec.push({ struct Foo(); Foo() }); // l3: Foo@l3 ≠ Foo@l2 => Error!
But you can do:
let mut vec = Vec::new();
for _ in 0 .. 2 {
vec.push({
pub struct Foo(); // Single global/compile-time definition
Foo() // runtime: instanced multiple times
});
}
since, modulo scoping, this is equivalent to:
mod compiler_generated {
pub mod for_loop_body {
pub struct Foo();
}
}
fn … (…)
-> …
{
…
let mut vec = Vec::new();
for _ in 0 .. 2 {
vec.push(compiler_generated::for_loop_body::Foo());
}
…
}
Compare that to the "unsugaring" of the one with the two struct Foo();
definitions:
mod compiler_generated {
pub mod l2 {
pub struct Foo();
}
pub mod l3 {
pub struct Foo();
}
}
fn … (…)
-> …
{
…
let mut vec = Vec::new();
vec.push(compiler_generated::l2::Foo()); // l2
vec.push(compiler_generated::l3::Foo()); // l3: Error!
…
}
Well, this is the exact same mechanism with closures: every time one writes a literal |…| …
closure, it's actually sugar for
{
struct Closure<…> { /* captured environment / "upvars" */ }
impl<…> Fn…(…) -> … for Closure<…> {
/* Make it `()`-callable */
}
Closure { /* upvars */ }
}
If the literal is written once inside a for
loop, then there shall be only one struct Closure… impl … Closure…
global/compile-time definition, as well as its Closure { /* upvars */ }
runtime-instantiation, which can thus occur multiple times if the for
loop so does:
Whereas with vec.push(|| ()); vec.push(|| ());
, we have two literal closures, and so, two global/compile-time struct Closure…
definitions, and thus, two distinct types (each runtime-instanced once).