Pre-RFC: `Self` in nested functions

Several times I have tried to use self or Self in nested functions but the compiler stops me. Any reason not to allow this? Nested functions (while they can be abused like anything else) are useful for code organization, but this is a papercut that deters their usage.

Perhaps it would be less controversial to just allow Self and not self. Because when I see the dot method call syntax, I expect to find that function as the direct child of some impl block.

struct X;

impl X {
    fn outer(&self) {
        fn inner(this: &Self, n: u32) {
            dbg!(?n);
        }
        inner(self, 1);
        inner(self, 2);
    }
}

After a little more thought, I realize that would imply pulling in other generic parameters. And functions are supposed to have a fresh environment of generics. Maybe there is no clean way to reconcile that :frowning: .

As far as I understand, there are no deep reasons for this behaviour, it's just the way the language is currently designed. There is nothing, in principle, preventing the inner functions from using the generics of their parent function. There may be some reasons related to macros (i.e. what if a macro expands to an inner function with generics), but it could also be solved by appropriate hygiene.

The workaround for your specific case is to declare the inner function as an ordinary method, use a local closure which doesn't capture the environment, or to introduce proper generic parameters instead of Self, i.e.

struct X;

impl X {
    fn outer(&self) {
        fn inner<T>(this: &T, n: u32) {
            dbg!(?n);
        }
        inner(self, 1);
        inner(self, 2);
    }
}

You can also add methods inside of a function by declaring an inner impl block:

struct X;

impl X {
    fn outer(&self) {
        impl X {
            fn inner(&self, n: u32) {
                dbg!(n);
            }
        }
        self.inner(1);
        self.inner(2);
    }
    
    fn another(&self) {
        self.inner(0);
    }
}

That has no benefits, though, because it's more verbose, and the supposedly inner method would still be usable in any other function which has visibility of that impl block (like X::another in the example above). There is no way to declare a method which would be visible only inside of a function (not unless you use a local extension trait, which just isn't worth the boilerplate). Again, while there are technical reasons for that design, I don't think there are any fundamental reasons why it couldn't work otherwise. But that may require a lot of work, and the benefits are quite moot.

2 Likes

Additionally, actually applying the outer generics to the inner function could easily break the (impl AsRef<str>) { fn inner(&str) { trick into compiling multiple copies of the inner function...