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?
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);
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.
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.
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();
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.
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});
}
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.
It is the full message. I only get a cannot assign to constant
error
No stack trace that points to some specific code?
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 Iām just going to requote this here.