Why do I need a (_:self) in a public function, defined in a struct, to make it become a member of that struct?

Hi

I have solved this issue but it seems very strange from a syntax point of view. I have tested in stable 12 and 13. Both have the same results.

I need to add _:Self to the input parm of a struct function to get it to work. Otherwise it does not compile as a member function.

You get

problem_struct.zig:21:7: note: 'print4' is not a member function
  pub fn print4 ()         void { std.debug.print("{s}, \n", .{Self.array[0]}); }

While this works fine

pub fn print3 (_:Self) void { std.debug.print("{s}, \n", .{Self.array[0]}); }

I understand why this works, from a compile point of view. But from a new coders point of view its weird that you have to declare something, Throw it away. So you can use it.

I guess I am asking whether anyone agrees? Is there a better way to enforce a function, which is defined in a struct, becomes a member function of the very struct it is defined in? Or do we do this for the greater good… the greater good. Grin

This is my first post so free to ignore.

All the best.

In my struct I have the following code extract

 13   // works. I invoke self, throw it away to _, then use it
 14   pub fn print3 (_:Self) void { std.debug.print("{s}, \n", .{Self.array[0]}); }

 15   // does not work (but compiles if not called, I assume because of the lazy compile)
 16   pub fn print4 ()       void { std.debug.print("{s}, \n", .{Self.array[0]}); }

The calling code extract

 16 // run a public function of the object
 17 my_object.print3();
 18
 19 // will not work, error below even though it is almost identical
 20 my_object.print4();

The error

 23 problem.zig:19:10: error: no field or member function named 'print4' in 'problem_struct.CreateStruct1(u8)'
 24 my_object.print4();
 25 ~~~~~~~~~^~~~~~~
 26 problem_struct.zig:9:10: note: struct declared here
 27   return struct {
 28          ^~~~~~
 29 **problem_struct.zig:21:7: note: 'print4' is not a member function**
 30   pub fn print4 ()         void { std.debug.print("{s}, \n", .{Self.array[0]}); }

My understanding is that _=variable simply throws it away. ie send to /dev/null. But then I use it.

The code in full

cat problem.zig

// This is the calling code and is in the file problem.zig

const std = @import("std");                // get the standard lib

const Mystruct = @import("problem_struct.zig").CreateStruct1;

pub fn main() void
{

//create a struct with type u8 in memory by running the function
const build_my_struct=Mystruct(u8);// _=build_my_struct;

// create my object with fresh struct data from the u8 struct above
const my_object=build_my_struct.new(10);//_= my_object;

// run a public function of the object
my_object.print3();

// will not work, error below even though it is almost identical
my_object.print4();

} // end of main

The Struct function

cat problem_struct.zig

// this code is stored in the file problem_struct.zig

const std = @import("std");                // get the standard lib

pub fn CreateStruct1(comptime mytype: type) type
 {

  return struct {

  const Self = @This();
  const array=[_][]const u8{"hello","This is a test"};

  // works
  pub fn print3 (_:Self) void { std.debug.print("{s}, \n", .{Self.array[0]}); }
  // does not
  pub fn print4 ()       void { std.debug.print("{s}, \n", .{Self.array[0]}); }


  // support to create the struct
  pub fn new(x:mytype) Self {return .{.x_co=x};} // namespace function ie self:
  x_co: mytype = 0, // random member

  }; // end of struct

 } // end of fn

Hi @endlessly_amused Welcome to ziggit :slight_smile:

I edited your post to mark the code blocks using ```

object.function() notation is equivalent to function(object)
with print3(_: Self) you can use this notation, but not with print4().

print4 is declared inside the struct of the type.
You can call it as: build_my_struct.print4();

In the Language Reference section on structs, you’ll find a comment that says:

// Structs can have methods
// Struct methods are not special, they are only namespaced
// functions that you can call with dot syntax.
2 Likes

Sorry, I will know for the future …

1 Like

I do think that bit of documentation could be improved somewhat. The actual rule is more complex: struct fns with a receiver-compatible argument (a value of the struct being defined, or a pointer to same) can be called from instances, but if the first argument is incompatible, then you have to call it from the type.

const SomeStruct = struct {
    a: u64 = 23,

    pub fn memberFn(self: *const SomeStruct) u64 {
        return self.a;
    }

    pub fn typeFn(val: u64) SomeStruct {
        return SomeStruct{ .a = val };
    }
};

test "struct behavior" {
    // These two are fine
    const sSt = SomeStruct.typeFn(45);
    try expectEqual(45, sSt.memberFn());
    // This is not:
    const sSt2 = sSt.typeFn(77);
    try expectEqual(77, sSt2.memberFn());
    // error: no field or member function named 'typeFn' in 'example.SomeStruct'
}

This could be spelled out much more clearly than it currently is. The documentation doesn’t actually indicate that you can’t call .init off of a member, only .dot.

Zig is not my first language by a long shot, and it wasn’t hard for me to grok the distinctions here. But it is one of a few corners of the language which is almost “magic”, in that the difference between .memberFn and .typeFn isn’t syntactically marked, it’s inferred from the type of the first argument. It would be worth a paragraph explicitly describing the rule here; it isn’t that complex, but any stumbling block for newcomers which can be removed, should be.

3 Likes