4

I'm just getting started with Zig and come from C++ and Rust;

I've been struck early with a difficult issue that I cannot seem to solve. Or find anywhere on the internet.

This is what I have:

// this doesn't work
pub const User = struct {

  bot:       bool,
  id:        *const [*:0]u8,
  username:  *const [*:0]u8,

  pub fn init() User {
  
    return User {

      .bot      = false,
      .id       = "THIS_IS_ID",
      .username = "THIS_IS_USERNAME"
    
    };
  }

  ...

}

const user = User.init();


// this works vvv
id: *const [10:0]u8,
.id = "THIS_IS_ID",

This is the error I get:

error: expected type '*const [*:0]u8', found '*const [10:0]u8'
  .id = "THIS_IS_ID",

My objective that I'm trying to get out of asking this question is to know if it's possible to have dynamic strings in zig; and if so, how so? I've seen some custom String structs online but was wondering if there is a way to achieve this without creating a separate type / struct for it..?

Thanks for the help!

gavin
  • 1,224
  • 5
  • 17

3 Answers3

3

Use []const u8 or []u8. See documentation for slices.

What const changes:

// Can assign string literals
var id: []const u8 = "THIS_IS_ID";
// vs
// var id: []u8 = "THIS_IS_ID";
//                ^~~~~~~~~~~~
// error: expected type '[]u8', found '*const [10:0]u8'
// note: cast discards const qualifier

// But cannot change individual bytes
std.mem.copy(u8, id, "THIS_IS_ANOTHER_ID");
//               ^~
// error: expected type '[]u8', found '[]const u8'
// note: cast discards const qualifier

You'd have to decide what owns the memory:

  • It could be each individual variable, or a field.
  • Or a separate memory buffer, or an allocator.
sigod
  • 3,514
  • 2
  • 21
  • 44
0

I don't really know too much about the problem scope (or zig either), but here's what I think you need to do:

  • for dynamic string (allocation) in zig, you need to ensure that you have an allocator to allocate some memory for the string.
// you can use any allocator you prefer
const allocator = std.heap.page_allocator;
  • you then need to actually allocate some space for the string you want to store
// here we allocate some memory for the string we want to store
// we allocate some memory for 10 bytes (which can store
// a string having at most 10 characters)
// NOTE: the operation could fail, hence the `try`
var slice = try allocator.alloc(u8, 10);
  • and finally, write the string you want to store into the allocated buffer/slice
// we write the string we want to store into the slice (or
// buffer) we previously allocated above
// NOTE: the operation could fail, hence the `try`. Also,
//       the function returns a slice of the buffer printed to.
_ = try std.fmt.bufPrint(slice, "hello", .{});

// you can print out the contents of the slice (or buffer)
std.debug.print("{s}", .{slice});
Adophilus
  • 172
  • 1
  • 11
0

As noted before, it looks like you want immutable .id and .username strings. In that case you can do:

struct {
    id: []const u8,
    username: []const u8,

… instead of:

struct {
    id: *const [*:0]u8,
    username: *const [*:0]u8,

… and then you can assign them to literals like `.id = "THIS_IS_ID".

If you do want mutable strings, then use dupe from std.mem.Allocator as follows.

    const id = try allocator.dupe(u8, "THIS_IS_ID");
    errdefer allocator.free(id);
    const username = try allocator.dupe(u8, "THIS_IS_USERNAME");
    return User {
      .bot      = false,
      .id       = id,
      .username = username,
    };

Zig forces you to explicitly take ownership of the new memory this way.

Pascal de Kloe
  • 523
  • 4
  • 12