Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass a C string into a Zig function expecting a Zig string?

Tags:

c

zig

Trying to use a Zig library expecting a string... but I get a string buffer from a C library.

That means that I need to pass a value of type [*c]u8 to a function that accepts [:0]const u8.

How to do that?

I found this way so far:

const buffer: [*c]u8 = callC();
const str = std.mem.span(@ptrCast([*:0]const u8, buffer));

Which looks more complicated than it should (and makes a copy??).

The Zig docs says that:

String literals are const pointers to null-terminated arrays of u8.

So I thought they are compatible C strings and a very simple cast like @as([*:0]const u8, buffer) should suffice?

like image 729
Renato Avatar asked Apr 22 '26 07:04

Renato


1 Answers

Which looks more complicated than it should (and makes a copy??).

So I thought they are compatible C strings and a very simple cast like @as([*:0]const u8, buffer) should suffice?

The issue here is that there is a difference between [*:0]u8 and [:0]u8

  • [*:0]u8 is a pointer with an unknown size ending in a 0. To determine its length, you have to loop over it and find where it ends.
  • [:0]u8 is a slice containing a pointer and a length. You can think of it like struct {ptr: [*:0]u8, len: usize}. This allows code using it to immediately know its length without having to loop over it.

@pointerCast is not required to convert from a [*c]u8 to a [*:0]u8:

const std = @import("std");

test "convert c string to [*:0]u8" {
    const c_string: [*c]const u8 = "some c string";
    const as_ptr: [*:0]const u8 = c_string;
    _ = as_ptr;
}

To get a zig-style string slice ([]const u8 or [:0]const u8), you can use the standard library function std.mem.span(ptr):

const std = @import("std");

test "convert c string to zig string" {
    const c_string: [*c]const u8 = "some c string";
    const as_slice: [:0]const u8 = std.mem.span(c_string);

    try std.testing.expectEqualStrings(as_slice, "some c string");
}

const buffer: [*c]u8 = callC();
const str = std.mem.span(@ptrCast([*:0]const u8, buffer));

Which looks more complicated than it should (and makes a copy??).

std.mem.span does not make a copy of the string - no standard library function will make a copy of something unless you pass it an allocator.

What it does is counts the length of the string and then returns a slice of the same memory, this time including a length.

like image 50
pfg Avatar answered Apr 23 '26 21:04

pfg



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!