Fn newMenu(vitem: std.ArrayList([] const u8) Help passage parameter

Hello,

  pub const MENU = struct {

    name : [] const u8,
    posx : usize,
    posy : usize,
    lines: usize,
    cols : usize,

    cadre: dds.CADRE,
    mnuvh : dds.MNUVH,

    attribut: AtrMnu,

    item: std.ArrayList([] const u8),
    selMenu : usize,
    actif: bool
  };

rcd_Menu.append(mnu.newMenu(
                        "Menu01",
                        1, 1,
                        5,
                        10,
                        mnu.AtrMnu,
                        dds.CADRE.line1,
                        dds.MNUVH.vertical,
                        mnu.item.{"Panel","SAV.","Exit"},  error???
                        )) catch unreachable ;

is it possible to put in the parameters a sequence of string please

I am not sure the following code is exactly what you need, but here it is:

const std = @import("std");
const Menu = std.ArrayList([]const u8);

fn printMenu(menu: *Menu) void {

    var k: u32 = 0;
    while (k < menu.items.len) : (k += 1) {
        std.debug.print("{s}\n", .{menu.items[k]});
    }
}

pub fn main() !void {

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    var menu = Menu.init(allocator);
    var ptr = try menu.addOne();
    ptr.* = "item_1";
    ptr = try menu.addOne();
    ptr.* = "item_2";
    ptr = try menu.addOne();
    ptr.* = "item_3";
    printMenu(&menu);
}
1 Like

Or like this, less wordily:

const std = @import("std");
const Menu = std.ArrayList([]const u8);

fn printMenu(menu: *Menu) void {

    var k: u32 = 0;
    while (k < menu.items.len) : (k += 1) {
        std.debug.print("{s}\n", .{menu.items[k]});
    }
}

pub fn main() !void {

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    var menu = try Menu.initCapacity(allocator, 3);
//    std.debug.print("len = {}\n", .{menu.items.len});
//    std.debug.print("cap = {}\n", .{menu.capacity});
    menu.items.len = 3; // not sure if it is quite "legally", but without this it does not work
    menu.items[0] = "item_1";
    menu.items[1] = "item_2";
    menu.items[2] = "item_3";
    printMenu(&menu);
}
1 Like

Probably it is better to use append():

    var menu = try Menu.initCapacity(allocator, 3);
    try menu.append("item_1");
    try menu.append("item_2");
    try menu.append("item_3");
1 Like

Thank you, you gave me ideas

I’m confused by what you’re trying to achieve here and what the problem actually is.

Is the issue that you’re attempting to initialize a std.ArrayList() inline as a parameter to this function? If you don’t need dynamic resizing of the slice of strings, you can just store and receive a []const []const u8, and pass it in like &.{"one", "two", "three", ...}. Alternatively, you can still pass it in as a static slice, initialize a std.ArrayList() and then calling .appendSlice() on it with the slice you’ve just received – this copies (and duplicates) memory but I suppose it’s not a big concern with these small sizes you’re handling from the above example. :slight_smile:

If instead you have a pre-owned, dynamically-allocated slice, then I suppose you could still pass in a slice and take ownership of it with std.ArrayList(...).fromOwnedSlice() in your initializer, though note that this requires also passing in the allocator that was used to dynamically allocate this slice. If you want to transfer ownership of an already existing std.ArrayList(), you can pair this with std.ArrayList(...).toOwnedSlice() which is essentially the inverse operation.

1 Like

Well, I just tried to help the author with passing an instance of ArrayList([]const u8) as a parameter, and did not think much about initializing as you noted.

As to your suggestions.

const std = @import("std");
fn printMenu(menu: []const[]const u8) void {
    for (menu) |item| {
        std.log.info("{s}", .{item});
    }
}
pub fn main() void {
    printMenu(&.{"item-1", "item-2", "item-3"});
}

Cool. It looks a bit like a magic (for me), due to & operator. As I guess taking the address of a tuple will coerce it to a slice?..

Did you mean this?

const std = @import("std");
const Allocator = std.mem.Allocator;
const Menu = std.ArrayList([]const u8);

fn printMenu(menu: []const[]const u8, allocator: Allocator) !void {
    var m = try Menu.initCapacity(allocator, menu.len);
    try m.appendSlice(menu);
    var k: u32 = 0;
    while (k < m.items.len) : (k += 1) {
        std.log.info("{s}", .{m.items[k]});
    }
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();
    try printMenu(&.{"item-1", "item-2", "item-3"}, allocator);
}

… am I feeling the smell of Rust right now? :slight_smile:

The problem is that https://ziglang.org/documentation/0.10.0/std/#root;ArrayList.fromOwnedSlice
is empty, https://ziglang.org/documentation/0.10.0/std/#root;ArrayList.toOwnedSlice is empty too.

Using these two functions (how, when, why) is not obvious at all.

Kind of. .{ ... } is just an anonymous list literal, and `& takes its address, which allows for it to be coerced into a slice (a slice is just a pointer + a length). See Documentation - The Zig Programming Language and Documentation - The Zig Programming Language.

Basically, but I was thinking of the case where the caller already has a std.ArrayList() at hand, and you want to pass it in to the initialization of a struct that also wants to hold a std.ArrayList() and it’s fine to duplicate their contents, so:

const Menu = struct {
  allocator: ...,
  arrayList: std.ArrayList(T),

  pub fn init(allocator: ..., items: []const []const T) !@This() {
    var a = std.ArrayList(T).init(allocator);
    try a.appendSlice(items);

    return .{
      .allocator = allocator,
      .arrayList = a,
    };
  }
};

pub fn main() !void {
  // ...
  var a = std.ArrayList(T).init(allocator);
  defer a.deinit();
  try a.append(blargh);
  try a.append(blorgh);
  try a.append(blirgh);

  const m = try Menu.init(allocator, a.items);
  defer m.deinit();
  // ...
}

Basically, pass around a slice still (via the .items field of the std.ArrayList()) and duplicate its contents.

Well, ownership is not unique to Rust, Rust gives an interesting twist by making the knowledge about the ownership of the various items explicit to the programmer and the compiler, as opposed to just the programmer. I’ve seen countless times something like this in a large C++ codebase:

class Thing {
public:
  Thing( ... );

private:
  AnotherThing* another_thing;  // owned  <-- this is a load-bearing comment :P
};

In Zig we don’t have a borrow checker or explicit ownership, so you need to be careful. But it’s so much easier than in C/C++: whenever something needs to allocate memory, you’ll see an allocator being passed around, so you know you’ll have to .deinit() accordingly later. Transfering ownership with fromOwnedSlice() / toOwnedSlice() is the exceptional case here, but the naming makes it clear who transfers ownership and who takes ownership of the data.

As for the lack of documentation: yes, autodoc is still being worked on. Until then, your best friend is the stdlib source code (which is not scary at all! it reads very clearly). See the code for fromOwnedSlice() and for toOwnedSlice().

2 Likes

I did similar things in C, so Zig’s approach is perfect for me. I had simple free list allocator (with malloc() backend) and used it for some linked list implementation - it performed ~3x faster than malloc() based list. Sorry for off-topic :slight_smile:

@jpl, just in case, variant without ArrayList:

const std = @import("std");

const Menu = struct {
    name: []const u8,
    items: []const[]const u8,

    fn init(name: []const u8, items: []const[]const u8) Menu {
        return Menu {
            .name = name,
            .items = items,
        };
    } 

    fn show(m: *Menu) void {
        std.debug.print("{s}\n", .{m.name});
        for (m.items) |item| {
            std.debug.print("  {s}\n", .{item});
        }
    }
};

pub fn main() void {
    var m = Menu.init("File", &.{"Open", "Save", "Save as.."});
    m.show();
}
1 Like

thank you, I like it, because I think of my code generator at the same time as I rewrite my code from Nim-lang and from time to time, I stumble, because I would not like to sink into algorithmic delusions. And at the same time I learn the grammar of Zig-lang

me too :face_with_diagonal_mouth:

I changed my code, with the last example it’s much simpler, I can’t publish it in GitHub - AS400JPLPC/zig_TermCurs: Terminal access function Zig-Lang and Generator because I’m on the GRID and I would like to finish displaying the GRID