Slightly odd error regarding scopes

So I was busy factoring out some common code from a couple of associated functions into function of its own like so :

struct MyStruct {
}

impl MyStruct {
    fn local_function() {}

    fn method_1(&mut self) {
        // Stuff...
        local_function();
        // More stuff...
    }

    fn method_2(&mut self) {
        // Stuff...
        local_function();
        // More stuff...
    }
}

When I got the following error:

error[E0425]: cannot find function `local_function` in this scope
 --> src/main.rs:9:9
  |
9 |         local_function();    
  |         ^^^^^^^^^^^^^^ not found in this scope

This seemed a bit odd to me as the function is right there, in the scope of the impl block of MyStruct. It's simple enough to fix by moving the function out of the impl block but I don't want to move it so far away from where it is used. Or call it with MyStruct::local_function()but that is adding noise.

To me it seems similar to the situation of defining an inner function within the scope of a function like so:

fn outer_function () {
    fn inner_function() {}

    inner_function();
}

Which works fine. One does not have to write outer_function::inner_function() or whatever to find the thing.

If find calling associated functions as Self::local_function() not noisy, as it is quite similar to how you'd call a method self.local_method().

3 Likes

Notice that the code is not similar: the function is defined inside the other function, so it really has the same scope.

I think you can simply think that impl is only a way to provide a "namespace" to methods. To say they belong to a structure. So you can use Self::thefunction, of x.the_method() if it references &self.

Ah OK.

I like Self::local_function(). Which is actually way the error message suggests. When I read it properly!

So not all scopes are scopes. Or a scope is not a scope when it's a namespace Which makes "not found in this scope" a bit odd.

scope is not a namespace. A namespace is the full name of a thing.

Scope is where a name does apply.

So, another way to think about is that impl is about namespaces, and not scopes, and that methods inside an impl block doesn't share scope.

1 Like

That all kind of makes sense.

But when naively looking at things within curly braces, within curly braces, within curly braces... it still seems somewhat odd. For example compare to this:

fn f2() {
    println!("Outside");
}

fn f1 () {
    fn f2() {
        println!("Inside");
    }
    fn f3() {
        f2();
    }
    f3();
}

Here the version of f2() inside f1() is what gets run. To my mind:
the fn f1() { is analogus to the impl block,
the fn f2() and fn f3() are analgus to my local_function() and method_1() above,
and the call f3() finds it without any extra help to resolve things.

Yeah, it is unfortunate the Name resolution and Scopes chapters of the reference aren't written yet. The closest thing to documentation on how name resolution works in different kind of scopes is the developer guide, which states (emphasis by me):

A name is visible only in certain area in the source code. This forms a hierarchical structure, but not necessarily a simple one ‒ if one scope is part of another, it doesn't mean the name visible in the outer one is also visible in the inner one, or that it refers to the same thing.

So basically the only answer I can give you based on the documentation I could find: name resolution is complicated.

1 Like

Oh good, I cane forgiven for my confusion then :slight_smile:

Interesting, I presented the opposite problem. The name is in the inner scope (The impl block) but still not visible.

Oh well, never mind, it's not a problem really.

I think the best way to think about this is that an impl is a very special thing in multiple ways. In particular, it isn't a scope for items; it never matters to name lookup whether two items are in the same impl or separate impls. Everything you declare in an impl gets associated with the specified type instead of participating in lexical scope. And if the impl is a trait implementation, then you can't even put arbitrary items in it at all.

// module scope for items 

impl Foo {
    // not a scope (except for generics on the impl)

    fn bar() {
        // new scope for variables and items
1 Like