Passing function with args as arg of other function gives error

Hi,

first of all, I haven’t dealt much with this Zig possibilities of comptime and passing functions and arguments as args itself. Thus, its very possible that I’m overlooking something obvious here. If thats the case, sorry for the wasted time :grin:

I’m building a binary for syncing data between two storage systems. To implement a retry system for specific kind of request errors, which can occur with any of the API-calling functions, I wanted to use a wrapper function which receives the particular called API function and its args as args itself. In general the idea was to use the wrapper function like std.Io.async() which also receives a function and its arguments as input and handle the specific error code to retry the API function a few times if necessary.

The wrapper function looks like this:

fn sendS3Request(comptime function: anytype, comptime args: std.meta.ArgsTuple(@TypeOf(function))) !@typeInfo(@TypeOf(function)).@"fn".return_type.? {
    var count = 1;
    while (true) : (count += 1) {
        const api_call_result = @call(.auto, function, args) catch |err| {
            switch (err) {
                .ReceiveFailed => if (count < 6) continue,
                else => return err,
            }
        };
        return api_call_result;
    }
}

However if I call this function e.g. with the following line:

// var get_obj = try source_client.getObject(source_buck, source_key, .{});
var get_obj = try sendS3Request(source_client.getObject, .{ source_buck, source_key, .{} });

I get this error saying getObject is no field:

install
└─ install rds_mig
   └─ compile exe rds_mig Debug native 1 errors
src/s3_commands.zig:83:51: error: no field named 'getObject' in struct 's3_client'
    var get_obj = try sendS3Request(source_client.getObject, .{ source_buck, source_key, .{} });
                                                  ^~~~~~~~~
/home/lukeflo/Documents/coding/projects/z3/src/s3_client.zig:1:1: note: struct declared here
//! Async S3 Client for Zig 0.16+
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

And thats correct, it is no field but a function exported inside the s3_client.zig file of the z3 S3 lib I’m using for the S3 API calls. But the outcommented version works.

Inside the library the function is defined in s3_ops.zig which then is exported in s3_client.zig with pub const getObject = ops.getObject, while the S3Client struct of the s3_client.zig file itself is exported inside the root.zig of the lib. My guess is, that all these reexports may have something to do with this compile error…

I don’t really understand why the programm is looking for a field name here and not the exported function…

Thankful for any help as its also the next learning step in my Zig-journey :slightly_smiling_face:

I’m assuming that source_client is an instance of S3Client. Declarations are part of the type itself, not of individual instances (that’s what fields are for). So you probably want this:

var get_obj = try sendS3Request(S3Client.getObject, .{ &source_client, source_buck, source_key, .{} });
1 Like

Basically it tells you that there is no field because you are using field access, when you actually call the method then you are using method call syntax, methods are called on an instance, but if you don’t call the method you need to access it on the type of the instance. So I think you could just use @TypeOf to access it:

var get_obj = try sendS3Request(@TypeOf(source_client).getObject, .{ source_buck, source_key, .{} });
1 Like

Oh man… Sometimes its so obvious, but I was tackling the issue from the wrong side…

Thanks both of you. Since the answer is rather the same, I’ll accept the first.