rustlanguage-lawyer

Scope of struct definition defined in a closure


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.

Solution

  • 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.