Anonymous modules

What if this syntax was allowed:

mod _ {
   /* implementantions */
}

This could be useful for proc-macro authors, as they can use the namespacing for hygiene. For example, they would be able to create a helper structs without potentially polluting the namespace, or exposing them to the user. As far as I know there aren't any drawbacks, but there may not be such a big need for such a thing.

How would you name anything inside of such a module from outside? If you don’t need the ability to name anything inside of the module, you can already use

const _: () = {
    // definitions, trait impls, etc…
};
15 Likes

Well I didn' know this was possible. As I conceived it, this would be no different to that.

I was scratching my head for a while before I understood it. Nice! But I'd prefer mod _ more, because that was clear to me immediately. However, as you mentioned, I'm missing the point how that could be used for anything useful :thinking:

Oh, no, for sure there's a point. I didn't want to suggest otherwise. This pattern can be useful for creating trait implementations; you can define some structs, types, functions that you're going to use internally in the trait impl in that module (this is particularly useful if you want to use some generated struct as an associated type for the trait implementation) - no need to be able to name anything inside of the module from outside of the module.

Note that this pattern is indeed already sometimes used in macros that generate trait implementations. I know about it from seeing it in some crate; unfortunately at the moment I don't quite remember which crate that was.

Edit: Ah, here's one: serde uses the pattern for their derive macros. E.g. click "Tools->Expand Macros" in their example playground.

2 Likes

I would really like mod {...} to be a "module expression" that desugars to a named (though the name is inaccessible) module defined somewhere else and just referred. Not sure if I explained it correctly, but it'll be easier to explain with an example:

use mod {
    pub struct Foo;
    pub struct Bar;
    pub struct Baz
}::{Foo, Bar};

This will bring Foo and Bar into scope, with Baz only accessible inside the module because even though it is public - the module cannot be named.

Of course - that part is not very useful. But consider this:

fn foo() -> mod {
    pub struct Foo {
        // ...
    }

    impl Foo {
        // ...
    }
}::Foo {
    // function body
}

Which is also not super useful - unless used in a macro:

fn foo() -> make_struct!(/* data used for creating Foo */) {
    // ...
}
2 Likes

That's an interesting idea. I think that the current const _: () = {/* ... */}; hack covers most use cases, but the ability to export functions can be useful for some corner cases. I imagine that it can be used to avoid shadowing statics among other things.

1 Like

Ok so one difference between const _: () = {/* ... */ } and mod _ { /* ... */ } would be that inside mod the namespace would be completely empty, so something like this would not throw an error:

const pi: f64 = 3.14; 
mod _ {
   fn foo() {
        let pi = 22.0/7.0; 
   }
}

while this does:

const pi: f64 = 3.14; 
const _: () =  {
   fn foo() {
        let pi = 22.0/7.0; 
   }
}; 

Other than that there would be no difference

1 Like

Good point, totally missed that! However, realistically in the application of writing macros, I'd assume you'll most often actually want to be able to access everything that is in scope in the surrounding scope. Adding use super::* helps in this case though. Actually, it's not quite the same, as that only imports things from the surrounding module, not everything in-between, e.g. items defined in the surrounding block in a function or a const.

I guess we're actually lacking proper support for importing "every item that's in scope" from where a module is defined inside of that module.

Example

fn f() {
    struct S;
    const _: () = {
        fn foo() -> S { S } // works
    };
    mod m {
        use super::*; // <- we might want an alternative
        // to `use super::*` that DOES make the following work
        fn foo() -> S { S } // doesn't work
    };
}
1 Like