How to bind into a function parameter

How do you bind a value to a variable passed into through a function in Zig? Like in the gorm package of the Go programming language

Could you provide some examples of what you mean? Are you referring to function currying?

1 Like

Like when you pass in an out argument and the result will be the out argument itself

The only way to modify a value passed into a function is via a pointer to that value. For example:

fn addOne(ptr: *u8) void {
    ptr.* += 1;
}

var x: u8 = 42;
addOne(&x);
1 Like

What if I don’t know the type?

At this point, I really think some examples would be good. We can kind of takes stabs at it, but a picture is worth a thousand words.

anytype can take in pointers and you can ensure that they’re pointers when you go in. If you need runtime-type information, there’s a variety of ways to do that, too.

If you could post the code that’s tripping you up (or that you’d like to replicate), then we can take a better shot at this.

1 Like

The problem is the code I wrote is way different from what I am requesting. Because the code I wrote returned an error and to my initial request was another way to solve my initial problem,

pub fn getField(comptime node: Node, comptime field_name: []const u8) !switch (node) {
            inline else => |n| {
                @TypeOf(@field(n, field_name));
            },
        } {
            return switch (node) {
                inline else => |n| @field(n, field_name),
            };
        }

This was the initial code. It worked for sometime but then after I updated my Zig version it keeps giving me comptime error. Version is 0.12.0-dev.3365+cbeab678a

Show more code, I am pretty certain there is a misunderstanding somewhere here.

Node is likely something you are intending to use at runtime, but why is it a comptime parameter then?

It is likely that you either want to operate on comptime Node: type or on node:Node.
comptime node:Node is realtively unlikely, except if you are doing very much (maybe too much) comptime programming.
So at least show us what Node is, but better a bigger example.

Else you force us to speculate about lots of different possible cases, which is not very productive.


The code you are currently showing doesn’t make much sense, because it can be reduced/fixed to:

const std = @import("std");

const Node = struct {
    foo: u32,
    bar: []const u8,
};

pub fn getField(comptime node: Node, comptime field_name: []const u8) @TypeOf(@field(node, field_name)) {
    return @field(node, field_name);
}

pub fn main() void {
    const val = getField(Node{ .foo = 4, .bar = "sdsds" }, "foo");
    std.debug.print("val: {}\n", .{val});
}

But this can be simplified to just @field:

pub fn main() void {
    const val = @field(Node{ .foo = 4, .bar = "sdsds" }, "foo");
    std.debug.print("val: {}\n", .{val});
}

It is likely you want something different.

1 Like

If you have examples from Go, that would probably be helpful too.

Not OP but I suspect they mean the Scan method on rows objects from the go SQL package

Where the signature is:

func (rs *Rows) Scan(dest ...any) error

And an example usage is for a query that returns:
"Hello", 12, 12.34

var s string
var i int64
var f float64
rows.Scan(&s, &i, &f)

I think it uses reflection to identify the type and populate the variables with the data.

For that the zig equivalent would be a tuple of pointers, something like this:

const Rows = struct {
     fn scan(rows:*Rows, args:anytype) !void {
        checkTupleOfPointers(@TypeOf(args));
        @compileError("TODO implement");
     }
};
var s: []const u8 = undefined;
var i: i64 = undefined;
var f: f64 = undefined;
try rows.scan(.{&s, &i, &f});

However I think in Zig a nicer way is to just give the function a type and return a value:

const Rows = struct {
     fn scan(rows:*Rows, comptime T:type) !T {
        var res:T = undefined;
        // if tuple try matching by position
        // @field(res, "0") = ...
        // if struct / union try matching field names
        @compileError("TODO implement");
        // return res;
     }
};
const res = try rows.scan(struct{s: []const u8, i: i64, f: f64});
// res.s to access

I presume that the Rows are in scope and have a longer lifetime anyway, so the returned struct can simply be a typed view into the data that is held by Rows.

This would even allow you to use destructuring:

const s, const i, const f = try rows.scan(.{[]const u8, i64, f64});

I guess if we ever get a way to write functions with inferred types at comptime, this also would be nice:

const s:[]const u8, const i:i64, const f:f64 = try rows.scan();
1 Like

Sorry, I have been a bit busy for a few days, but I have returned with an example you guys wanted me to give:

const std: type = @import("std");

pub fn bind_to_buf(buf: []u8) void {
    std.crypto.random.bytes(buf);
}

pub fn main() !void {
    var buf: [37]u8 = undefined;
    bind_to_buf(&buf);
    std.debug.print("{s}\n", .{buf});
}

When I run the code I get my expected result i.e. a randomly generated string, which in my case is
l 8�y��52�nC,?#�E�\�N���,s4kS��

But when I try to do it with a single variable I get a
cannot assign to constant error. I am looking for a way to fix the error.

Sorry for being vague in the initial doubt and for the lack of example and for my absence.

It is hard to tell without seeing the actual error, what is the full error message?

My guess is that (like in most programming languages) passing an array actually passes a pointer to the array, making it easy to change the contents of the original array.

Passing a “primitive” value, like an i32 or u8, will not allow you to modify the original value - as that is somewhere on the stack, and the function cannot locate it.

Quite some modern languages (like Zig or Kotlin) therefore forbid modifying parameters with primitive values in a method, as beginning or absent-minded programmers might mistakenly assume that they can change the input parameters. Hence you get the “const” error. (I actually had a Java exam today that tried to trap me with that question; Java is still from the time that you could ‘modify’ primitive parameters, giving rise to many confused developers and tricky programming exam questions)

The solution would be, as pointed out in one of the first replies, to use a pointer argument, so to pass an &u8 instead of a ‘normal’ u8.

2 Likes
const std = @import("std");

pub fn bind_to_ch(ch: *u8) void {
    ch.* = 'A';
}

pub fn main() !void {
    var ch: u8 = undefined;
    bind_to_ch(&ch);
    std.debug.print("{c}\n", .{ch});
}
1 Like

This works because &buf is a pointer to an array that you are passing to a function that expects a slice, because the length of the array is statically known the pointer can be coerced to a slice (a slice is a pointer and a length, length of the array it points to). The slice itself is constant (because it is a function argument), but it points to memory that isn’t constant, that is why the std.crypto.random.bytes can change the memory to random bytes.

When you want to write to a single variable, you need a pointer to that single variable like in @dimdin’s answer.

Also take a look at Mutable and Constant Pointer Semantics


Showing the actual error, would have probably solved this in the first answer.

By having problem y but asking about x, this turned into a XY problem - Wikipedia, to avoid this next time post the actual error and then add your additional questions to that.

It is a bit unsatisfying when you need to poke around at different possibilities about “what are we talking about?”

That said don’t feel bad about it, it is just that code plus error messages are really helpful to understand a lot of context, that can’t easily be replaced by descriptions.

1 Like

It is the full message. I only get a cannot assign to constant error

No stack trace that points to some specific code?

1 Like

We’re kind of at a loss here. Believe me, we’d like to help you, but we need to see code. You need to show us the function that isn’t working - the one that passes a single value and the context of how you’re using it.

@EWLameijer, almost for got to welcome you to the forum. Thanks for your contribution :slight_smile: I’m just going to requote this here.

1 Like