I expected the following code to not compile, but it did compile (playground):
fn main() {
(0..3)
.map(|i| {
struct S {
v: usize,
}
S { v: i }
})
.collect::<Vec<_>>();
}
I thought every closure call would define a new struct type (and its instance) so the result couldn't be collected as a vector of some single type, but it seems S
is not that local.
How does this work exactly under the hood? I want strict explanation such as a link to a page of The Rust Reference, so added #language-lawyer
tag.
If you literally read the code above without knowing how Rust works, I think there can be two interpretations as shown below. I thought the second interpretation was correct, but the fact the code compiles implies the first one is correct. But why? That's my question.
Interpretation 1
//`S` is *global* in some sense.
struct S {
v: usize,
}
//Every invocation of the closure just creates a new instance of the type `S`.
for i in 0..3 {
vec.push(S { v: 0 });
}
Interpretation 2
{
//Every invocation of the closure declares a new struct type
// which is local to the invocation.
struct __S_anonymous_01 {
v: usize,
}
//And it then creates a new instance of that type.
vec.push(__S_anonymous_01 { v: 0 });
}
{
struct __S_anonymous_02 {
v: usize,
}
vec.push(__S_anonymous_02 { v: 1 });
}
{
struct __S_anonymous_03 {
v: usize,
}
vec.push(__S_anonymous_03 { v: 2 });
}
//And `__S_anonymous_{01|02|03}` are all different
// though they have the same structure
// because Rust doesn't support duck typing.
https://doc.rust-lang.org/stable/reference/items.html
Items are entirely determined at compile-time, generally remain fixed during execution, and may reside in read-only memory.
(Items include structs).
Generally, rust does not define value-dependent types. Furthermore, items (including e.g. structs and functions) defined inside functions/closures cannot use values (e.g. variables) or even generic parameters from their parent, which strengthen the fact that being nested for items impact nothing other than visibility.