#Converting string to arbitrary length array

1 messages · Page 1 of 1 (latest)

civic comet
#

Wondering if there is a better way to do this:

var buf: [10]u8 = undefined;
const input = "hello";
for (input) |char, idx| buf[idx] = char;
buf[input.len] = 0;
// {'h', 'e', 'l', 'l', 'o', 0, undefined, undefined, undefined, undefined}
my_parsing_function(&buf);

I am using this to write tests that simulate input from stdin.

half talon
#

Using ArrayList could be bit nicer and wouldn't be much different considering it's for test purposes

scenic tiger
#

std.io.fixedBufferStream may be what you're looking for.

var fbs = std.io.fixedBufferStream(input);
const reader = fbs.reader(); // expoes all the methods that `std.io.getStdIn().reader()` does
civic comet
#

Thanks - will look into both

coarse pasture
#

Also worth noting that your loop is just std.mem.copy(u8, buf, input)

safe niche
#

Looks like you want a sentinel terminated array? Like c uses?

#

I'd say you need to make a slice of var slice = buf[0..input.len]

#

and then assign string to it

#

or just var slice = input[0..]???
and my_parsing_funciton(slice);

civic comet
#

I'm a bit confused. I think I want a slice that actually has the zero as the final value. But now that I saw what you wrote I am looking back at my_parsing_func and wondering if I should implement it differently though.

#
const stdin = std.io.getStdIn().reader();
var buf: [10]u8 = undefined;
if (try stdin.readUntilDelimiterOrEof(buf[0..], '\n')) |user_input| {
   my_parsing_func(&buf);
}

This is what I had. Which meant my_parsing_func expects an array with 10 bytes with a zero indicating where input stopped.

But now I'm realizing user_input is probably slice of exactly the relevant characters and nothing else. So I should just use that...

#

Ok this seems to work (with some changes inside the parse function)

var buf: [5]u8 = "hello".*;
const cmd = try parse_cmd(&buf);

I don't quite understand what .* does here - found it in the ziglings exercises on sentinels

#

It seems to discard the sentinel and just become a normal slice.

#

Doesn't seem like I can replace [5] with [_]

scenic tiger
# civic comet Ok this seems to work (with some changes inside the parse function) ``` var buf:...

to explain what's happening here:
strings literals are of type *const [_:0]u8, so e.g. @TypeOf("foo") == *const [3:0]u8. In english, we'd call that a "Pointer to a constant array of 3 elements and a sentinel of 0, with a child type of u8".
The thing about pointers to arrays in zig is that they are treated a bit specially, in that you can treat them almost identically to a slice, which is a useful property that allows easily going between using slices and pointers to arrays. But, besides that, they are ultimately just regular single-item pointers, and like all others, support dereferencing to either write to the memory location, or get a copy of it.
In this case, you dereference it, and get a copy of it, which has a type [_:0]u8. The reason it "loses" the sentinel is coercion. The relevant bytes are copied from the [_:0]u8 to the [_]u8, without the sentinel. This basically just happens because it's convenient, and it doesn't lose any information that can't be easily regained, or that is strictly necessary.
And thus, it doesn't "become a normal slice", the array has just been coerced.

Besides that, the reason you can't do var buf: [_]u8 is because [_]T is syntax that is only for inferring the length of a literal, like [_]u32{ 1, 2, 3 } - zig doesn't really infer patterns.

Also, another place you'll have been using pointers to arrays without realising it is the slicing operation: if you do array[0..], where array is of a type like [n]T, the type of the result you'll actually get is *[n]T, and ditto for anything like ptr[n..m] where n and m are comptime-known.

civic comet
#

Ok - I think I am following this (thank you for the amazing explanation). So "hello" is implicitly creating a pointer to sentinel terminated array. "hello".* is dereferencing that pointer to get the underlying 5 bytes of h e l l o and then assigning that to a new variable of type [5]u8.

A pointer to an array (including one made implicitly in declaring a string a literal) does not have length information so zig can't do any magic there.

scenic tiger
# civic comet Ok - I think I am following this (thank you for the amazing explanation). So `"...

that last bit isn't quite on the mark. The string literal does in fact have length information, and you could just do e.g. var buf = "hello".*;, with the only caveat being that @TypeOf(buf) == [5:0]u8. What I was trying to explain was that zig doesn't do any sort of type pattern inference - meaning a variable type is either written explicitly, or it is inferred from the type of the value used to initialise it

civic comet
#

So inference is all or nothing essentially - you can't assign a specific type but expect it to still infer the length. Is that a better statement?

scenic tiger
#

aye, spot on