Cannot find function

In the following code, why do I have to write Self::function_name instead of just function name? I mean, seriously, this is impl block and if in this block there is a function with that name why the compiler cannot deduce which function I have in mind? (collect_primary is the name of the function).

pub struct Parser
{
    primary: String,
}

impl Parser
{
    pub fn parse(s:&str)
    {
        for a_char in s.chars()
        {
            match a_char
            {
                '0'..='9' => collect_primary(&a_char),**//Why this will not find collect_primary???**
                '+' | '-' => println!("We got a sign: {}", a_char),
                _ => println!("Not a number!")
            }
        }
    }
    fn collect_primary(ch:&char)
    {

    }
}

You are using the verbose way of calling it. Typically you would write it as self.collect_primary(&a_char).

I've edited the code so better illustrates what I'm asking about.

Put it outside the impl block.

But the whole point is that the function belongs to a Parser, but it is a private function. I don't want it to be a public, non Parser's function.

You don't have to make it public.

The syntax is different for calling an associated function and calling a free function. I don't personally think that it is unreasonable.

OK, I understand, but then, if I don't keep it in the impl block it somewhat looses the "belongs to" property and becomes a "free" function, which I wouldn't like to do.
I simply don't understand why the compiler complains about it.

In Rust, you always have to use self when accessing something on self. The language is just being consistent. It's the same for methods and fields.

Yes, I suppose so. TBH, seeing self everywhere in the code, drives me nuts.

I tend to prefer free functions, but didn't always. Coming to Rust after doing a lot of OO development, at first I too wanted my functions to 'belong' to a struct.

Realizing two things helped me change:

  1. Rust's level of encapsulation is not the struct or impl block, but the module. Free functions have total access to the struct's private fields and functions, and they do not have to be public at all. They can even be located in a sub-module and still have total access. This means encapsulation is a complete non-reason for choosing associated functions over free functions. Instead, reorganizing your modules to map closer to what you would consider an OO 'class' in other languages leads to better organized code in my opinion.

  2. If a function doesn't operate on a specific instance of a struct, then making it an associated function doesn't buy you much even in terms of syntactic sugar, and can actually hurt chances for reuse. This can prevent you from seeing the function might be useful outside the context you originally envisioned for it.

I tend to limit associated functions to only methods that take some form of self parameter, and constructors.

4 Likes

Then the impl block seems bit pointless tbh.
Also, thanks for making me aware that free functions do have an access to struct's private fields. I was totally unaware of that.

You still need impl blocks to declare methods and associated constants.

struct Foo {
    x: String,
}

impl Foo {
    pub const BAR: usize = 42;
    
    fn len(&self) -> usize { self.x.len() }
}

Surely:

fn len(f:&Foo) -> usize { f.x.len() }

would do the same?

The function you defined there would be called with len(&foo), not foo.len().

Functionally yes, but you would lose a lot of the benefits that method syntax gives you like method chaining and using the "object" mindset instead of a more procedural one.

Yes, I'm aware of that. I'm simply questioning point of having impl block where in fact everything can be done without it, or, its usage is of very, very limited point. (After what I've learned about free functions)

You wouldn't loose method chaining if you return &mut to that object.

If len is not a method, you can no longer write an expression like this as a method chain:

x.bar().foo().len()

Instead, you would need to write:

len(&x.bar().foo())

Tbh, the second form is more readable.