How do I pass a stream or writer parameter to a function in Zig? - zig

I'm trying to pass the output stream to a function but can't get it right. This sample code shows a couple of the things I've tried
// Attempts to pass stream or writer to a function
const std = #import("std");
pub fn main() !void {
// #1
try print1(std.io.getStdOut(), "Hello, ");
// #2
try print2(std.io.getStdOut().writer(), "world!");
}
// error: 'File' is not marked 'pub'
pub fn print1(file: std.io.File, str: []const u8) !void
{
try file.writer().print("{s}", .{str});
}
// error: expected type 'type', found 'fn(comptime type, comptime type, comptime anytype) type'
fn print2(writer: std.io.Writer, str: []const u8) !void
{
try writer.print("{s}", .{str});
}
I'm using Zig 0.10.0

The call to std.io.getStdOut() returns a File, but the File type is in the std.fs namespace. Calling std.io.getStdOut().writer() returns a Writer from the std.fs.File namespace. You could also declare the writer parameter with the anytype keyword to get type inference at the time of the function call.
Here is a modified version of OP posted code:
const std = #import("std");
pub fn main() !void {
const stdout = std.io.getStdOut();
const writer = stdout.writer();
// #1
// Pass `stdout` to a function:
try print1(stdout, "Hello, ");
// #2
// Pass a `Writer` to a function:
try print2(writer, "world!\n");
// #3
// Pass a `Writer` to a function:
try print3(writer, "Hello, again!\n");
}
fn print1(file: std.fs.File, str: []const u8) !void {
try file.writer().print("{s}", .{str});
}
// Explicit type annotation for `writer`:
fn print2(writer: std.fs.File.Writer, str: []const u8) !void {
try writer.print("{s}", .{str});
}
// The type of `writer` is inferred when the function is called:
fn print3(writer: anytype, str: []const u8) !void {
try writer.print("{s}", .{str});
}
And a sample run:
$ zig run print_stream.zig
Hello, world!
Hello, again!

io.Writer is a generic data structure. I.e. it's a function that returns a type. You cannot use it as a function argument, but you can:
Use anytype.
Use an alias, like fs.File.Writer.
Use a "proxy" type, like fs.File, on which you'll call writer().
Use full specialization, like io.Writer(fs.File, fs.File.WriteError, fs.File.write).
anytype is required for functions that must accept arbitrary writers. Otherwise, it might be nicer to use an alias or a "proxy" type.

Related

Could someone explain how this works: const print = #import("std").debug.print;

How can this const print variable behave like a function?
const print = #import("std").debug.print;
print("hello, world", .{});
I understand you can assign expressions to variables. But this seems to behave like a precompiler macro in c/c++, I wouldn't have guessed that.
Is it because "all variables declared in a comptime expression are implicitly comptime variables" and # makes it a comptime expression, so it is evaluated before compilation, much like a macro would in c?
Could someone elaborate a bit? This seems a very powerful feature.
# does not indicate a comptime expression; rather # prefixes built-in functions in Zig. #import returns a struct that provides access to the declarations made public by the imported file.
The expression #import("std").debug.print evaluates to the print function defined in the standard library file std/debug.zig; it is not the expression that is assigned to print in the posted code, but the function. That is, the posted code works because print in OP code is actually a function. This can be seen by running the code below:
const print = #import("std").debug.print;
pub fn main() void {
print("#TypeOf print: {}\n", .{ #TypeOf(print) });
}
Results:
$ zig run print_type.zig
#TypeOf print: fn(comptime []const u8, anytype) void
Function Assignment
OP has asked for another example of assigning a function that is not imported to an identifier:
const print = #import("std").debug.print;
fn my_function(msg: []const u8) void {
print("{s}\n", .{ msg });
}
const my_function_alias = my_function;
pub fn main() void {
const another_function_alias = my_function;
const yet_another_function_alias = my_function_alias;
my_function("mf");
my_function_alias("mfa");
another_function_alias("afa");
yet_another_function_alias("yafa");
}
Program output:
$ zig run function_assignment.zig
mf
mfa
afa
yafa

Declaring pointer to function

I found some (a bit strange) feature of zig when played with function pointers.
Here is an example which I beleive is written as implied.
const std = #import("std");
const print = std.debug.print;
const aFunc = fn (x: i32) void;
fn theFunc(x: i32) void {
print("have {}\n", .{x});
}
pub fn main() void {
const f: aFunc = theFunc;
f(3);
}
This code compiles and runs Ok.
Now change it like this.
const std = #import("std");
const print = std.debug.print;
const aFunc = fn someFunc (x: i32) void;
fn theFunc(x: i32) void {
print("have {}\n", .{x});
}
pub fn main() void {
const f: aFunc = theFunc;
f(3);
}
This code is also Ok, but shouldn't the compiler emit error/warning about someFunc?
This name is useless - when I tried to use it instead of aFunc there was an compilation error.
Compiler version:
$ /opt/zig/zig version
0.10.0-dev.3431+4a4f3c50c
It looks like the zig source parser treats
const aFunc = fn someFunc (x: i32) void;
as if it is function definition, but silently drops someFunc.

Idiomatic way to wite/pass type to generic function in ziglang

This is a much simplified version of my real issue but hopefully demonstrates my question in a concise manner.
My question is about the interface to printKeys. I have to pass in the type of the data to be printed as a comptime parameter, and the easiest way to get this is to use #TypeOf on the map at the point of calling it.
Coming from C++ this seems slightly inelegant that the type can't be inferred, although I do like being explicit too.
Is there a more idiomatic way to have a generic function in zig that doesn't need this use of #TypeOf at the point of calling, or a better way to do this in general?
const std = #import("std");
fn printKeys(comptime MapType: type, data: MapType) void {
var iter = data.keyIterator();
while (iter.next()) | value | {
std.debug.print("Value is {}\n", .{value.*});
}
}
pub fn main() !void {
const allocator = std.heap.page_allocator;
var map = std.AutoHashMap(i32, []const u8).init(allocator);
defer map.deinit();
try map.put(10, "ten");
try map.put(12, "twelve");
try map.put(5, "five");
printKeys(#TypeOf(map), map);
}
use anytype. you can find more examples in zig docs (Ctrl + F) and search anytype
fn printKeys(data: anytype) void {
...
}
pub fn main() !void {
...
printKeys(map);
}

Identifying a function with a return type of void in runtime

I have a function that accepts another function and its arguments as arguments. The return type of the function and its parameter types are unknown. I need to know in runtime if the argument function returns a value or if it is a void function.
functionHandler(Function fn, List<dynamic> args) {
var result = fn(args[0], args[1]);
if ( -- fn returns a value --)
doSomething();
else if ( -- fn is a void function --)
doSomethingElse();
}
I can't use fn.runtimeType because the the parameter types are unknown.
A void function will return null, but then I can't distinguish between a void function and a function that simply returned a null value.
Is there any way I can check in runtime if fn is a function with return type void?
A hacky way would be to use fn.runtimeType.toString().endsWith('=> void').
Since you seem to know that fn takes exactly two positional arguments, a better way would be to make functionHandler a generic function, fully qualify your Function type with parameterized types, and then check the type parameter for the return type:
dynamic functionHandler<ReturnType, ArgumentType1, ArgumentType2>(
ReturnType Function(ArgumentType1, ArgumentType2) fn,
List<dynamic> args,
) {
// `ReturnType == void` is a syntax error, so we must do a little
// dance to get its corresponding `Type` object.
Type getType<T>() => T;
if (ReturnType == getType<void>()) {
print('void');
} else {
print('non-void');
}
}
void main() {
// Test a `void` function.
void voidFunction(Object arg1, dynamic arg2) {}
functionHandler(voidFunction, []); // Prints: void
// Test a function that returns `null`.
Null nullFunction(int arg1, String? arg2) => null;
functionHandler(nullFunction, []); // Prints: non-void
// Test a function with optional arguments.
int intFunction(int arg1, [int? arg2, int? arg3]) => 0;
functionHandler(intFunction, []); // Prints: non-void
}

Rust closure as callback for C bindings receiving garbage value in captured variable

I'm writing Rust wrappers for C bindings so that they look more Rusty. One such C function is this:
void mosquitto_connect_callback_set(
struct mosquitto * mosq,
void (*on_connect)(struct mosquitto *, void *, int)
)
I'm using the below technique to pass a Rust closure as the user data to above binding (void* in the callback) so that the Rust closure will be called when the C callback is invoked.
// Registered callback is called when the broker sends a CONNACK message in response
// to a connection. Will be called even incase of failure. All your sub/pub stuff
// should ideally be done in this callback when connection is successful
pub fn onconnect_callback<F>(&self, callback: F)
where F: Fn(i32)
{
// Convert the rust closure into void* to be used as user_data. This will
// be passed to callback automatically by the library
let cb = &callback as *const _ as *mut libc::c_void;
unsafe {
// Set our closure as user data
bindings::mosquitto_user_data_set(self.mosquitto, cb);
// Register callback
bindings::mosquitto_connect_callback_set(self.mosquitto, Some(onconnect_wrapper::<F>));
}
// Registered callback. user data is our closure
unsafe extern "C" fn onconnect_wrapper<F>(mqtt: *mut bindings::Struct_mosquitto,
closure: *mut libc::c_void,
val: libc::c_int)
where F: Fn(i32)
{
let closure = closure as *mut F;
println!("rc = {:?}", val as i32);
(*closure)(val as i32);
}
}
But the problem is that user data is set using a function instead of directly passing it to the callback set function
// Set our closure as user data
bindings::mosquitto_user_data_set(self.mosquitto, cb);
I think the callback: F closure passed to onconnect_callback might get destroyed by the time the actual C callback is invoked. This might be the reason I'm getting garbage values when capturing a variable.
let i = 100;
client.onconnect_callback(|a: i32|{
println!("i = {:?}", i);
println!("### On connect callback {}###", a)
});
match client.connect("localhost"){
Ok(_) => println!("Connection successful --> {:?}", client),
Err(n) => panic!("Connection error = {:?}", n)
}
OUTPUT:
i = 734146560
### On connect callback 0###
How do I fix this without passing closure as reference?
The full code

Resources