Renato
Renato

Reputation: 13710

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

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?

Upvotes: 10

Views: 8796

Answers (1)

pfg
pfg

Reputation: 3573

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.

Upvotes: 16

Related Questions