How to properly read from stdin?

So I have a couple issues:

const std = @import("std");
//const stdout = std.io.getStdOut().writer();
//const stdin = std.io.getStdIn().reader();
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
var allocator = gpa.allocator();

//fn run_file(path: []const u8) !void {
//
//}

fn run_prompt() !void {
    const stdout = std.io.getStdOut().writer();
    const stdin = std.io.getStdIn().reader();

    while (true) {
        try stdout.print("> ", .{});
        var buffer: [1024]u8 = undefined;

        const result = try stdin.readUntilDelimiter(&buffer, '\n');
        _ = result;
        try stdout.print("{s}", .{buffer});
    }
}

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();
    const args = try std.process.argsAlloc(allocator);
    if (args.len > 2) {
        try stdout.print("Usage: {s} [path_to_script_file]", .{args[0]});
    } else if (args.len == 2) {
        //run_file(args[1]);
    } else {
        try run_prompt();
    }
}
  1. The buffer array will successfully store input such as “test” but it will alsoa bunch of random characters.
    The output is as such:
> test
test
¬¬¬¬¬¬¬¬¬¬¬¬¬...

As far as I understand, this is because unlike C/C++, strings in Zig are not null terminated. So it’s printing the entire array. What I’m wondering is if there’s a better way of getting input from a user.

  1. I am unable to have global objects stdin and stdout. If I try to compile with these as global variables instead of variables scoped within a function, I get the following error:
   └─ zig build-exe Zrox Debug native 1 errors
C:\zig-windows-x86_64-0.14.0-dev.186+8f20e81b8\lib\std\os\windows.zig:2107:28: error: unable to evaluate comptime expression
                break :blk asm (
                           ^~~
C:\zig-windows-x86_64-0.14.0-dev.186+8f20e81b8\lib\std\os\windows.zig:2122:15: note: called from here
    return teb().ProcessEnvironmentBlock;
           ~~~^~
C:\zig-windows-x86_64-0.14.0-dev.186+8f20e81b8\lib\std\io.zig:23:27: note: called from here
        return windows.peb().ProcessParameters.hStdOutput;
               ~~~~~~~~~~~^~
C:\zig-windows-x86_64-0.14.0-dev.186+8f20e81b8\lib\std\io.zig:34:40: note: called from here
    return .{ .handle = getStdOutHandle() };
                        ~~~~~~~~~~~~~~~^~
src\main.zig:2:32: note: called from here
const stdout = std.io.getStdOut().writer();

Why does this happen?

1 Like

I believe it has to do with the difference between slices and arrays.

TL;DR: In run_prompt, replace

try stdout.print("{s}", .{buffer});

with

try stdout.print("{s}", .{result});

In your example, buffer is an array of 1024 elements. When you pass that to print, it’s going to print all 1024 elements.

readUntilDelimiter returns a slice of the buffer array, which includes only the bytes up to the delimiter.

Alternatively, you can replace

try stdout.print("{s}", .{buffer});

with

try stdout.print("{s}", .{buffer[0..result.len]});
3 Likes

Thanks. I thought readUntilDelimiter would instead be stored in the buffer and that I’d be printing from that. Silly mistake.

Do you have any idea as to what’s causing the second issue with declaring stdout and stdin globally?

1 Like

readUntilDelimiter is storing it’s data in the buffer, which is why you see it printed out before the garbage characters in your initial example. It’s returning a slice pointing to that data with a length.

I do not know what’s causing the second issue as I cannot reproduce it. I’m using 0.14.0-dev.364+8ab70f80c, so I suppose your version could have a bug. I would update to the latest master version and retry it.

2 Likes

I am also using 0.14.0-dev.186+8f20e81b8

EDIT: Strange. Now it seems to be working. Might’ve just been a weird one-off glitch.
EDIT 2: Nevermind, it’s still happening. I’ll open up a new thread for this.

It seems like a compiler bug for Windows targets relating to global constants. It does not happen on Linux but does on my Windows VM.

EDIT: Looks like this is a known issue limitation of Windows:

2 Likes

Well, guess what, just earlier today I did the exact same mistake of using my buffer variable instead of the slice returned by readUntilDelimiter!

Kinda funny to see I wasn’t the only one. Old habits of C coding, I suppose? :smile:

Have a good day!