# Should read return !?usize?

**URL:** <https://ziggit.dev/t/should-read-return-usize/9827>\
**Category:** Brainstorming\
**Tags:** language\
**Created:** [April 26, 2025, 2:57pm UTC](https://ziggit.dev/t/should-read-return-usize/9827 "2025-04-26T14:57:29Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![Zemogus](https://ziggit.dev/user_avatar/ziggit.dev/zemogus/32/1421_2.png) [@Zemogus](https://ziggit.dev/u/Zemogus)\
**Post date:** [April 26, 2025, 2:57pm UTC](https://ziggit.dev/t/should-read-return-usize/9827/1 "2025-04-26T14:57:29Z")

</div>

So I was happily coding along when copilot hallucinated this code:

```zig
var buf: [8192]u8 = undefined;
while (try de.reader().read(&buf)) |n| {
    _ = try stdout.write(buf[0..n]);
}

```

This obviously doesn’t compile because `read()` returns `!usize`. But it could work if `read()` returns `null` instead of `0`. Personally I feel like returning “null bytes read” doesn’t really make sense, but the resulting code does look more concise than the currently functional alternative:

```zig
var buf: [8192]u8 = undefined;
while (true) {
    const n = try de.reader().read(&buf);
    if (n == 0) break;
    _ = try stdout.write(buf[0..n]);
}

```

What do you guys think?

---

<div class="post-metadata">

**Author:** ![g41797](https://ziggit.dev/user_avatar/ziggit.dev/g41797/32/3339_2.png) [@g41797](https://ziggit.dev/u/g41797)\
**Post date:** [April 26, 2025, 4:48pm UTC](https://ziggit.dev/t/should-read-return-usize/9827/2 "2025-04-26T16:48:59Z")

</div>

according to [read(2) — Linux manual page](https://man7.org/linux/man-pages/man2/read.2.html):

> On success, the number of bytes read is returned (_zero_ indicates **end of file** ),

It means zero is valid value for read operation

_nullable_ has different meaning

---

<div class="post-metadata">

**Author:** ![dasimmet](https://ziggit.dev/user_avatar/ziggit.dev/dasimmet/32/6618_2.png) [@dasimmet](https://ziggit.dev/u/dasimmet)\
**Post date:** [April 26, 2025, 4:54pm UTC](https://ziggit.dev/t/should-read-return-usize/9827/3 "2025-04-26T16:54:18Z")

</div>

I’d say since read returns an Integer length it should be allowed to return 0. It’s supposed to be close to the C ABI version and 0 meaning end of stream is it’s convention.  
But maybe a separate function that returns `!?[]u8` would be a convenient wrapper that could be a “method” of the reader (for the common case of slicing buf):

```zig
fn readSlice(reader: anytype, type: T, buf: []T) !?[]T {
    const nread = try reader.read(buf);
    if (nread == 0) return null;
    return buf[0..nread];
}

```

separate function version:

```zig
var buf: [8192]u8 = undefined;
while (try readSlice(de.reader(), u8, &buf)) |slice| {
    _ = try stdout.write(slice);
}

```

as a “method” of reader the type argument could be omitted, too:

```zig
var buf: [8192]u8 = undefined;
while (try de.reader().readSlice(&buf)) |slice| {
    _ = try stdout.write(slice);
}

```

---

<div class="post-metadata">

**Author:** ![LucasSantos91](https://ziggit.dev/user_avatar/ziggit.dev/lucassantos91/32/440_2.png) [@LucasSantos91](https://ziggit.dev/u/LucasSantos91)\
**Post date:** [April 26, 2025, 4:54pm UTC](https://ziggit.dev/t/should-read-return-usize/9827/4 "2025-04-26T16:54:27Z")

</div>

```zig
var buf: [8192]u8 = undefined;
while (de.reader().read(&buf)) |n| {
    _ = try stdout.write(buf[0..n]);
} else |err| return err;

```

I think this would also work.

In any case, I agree with the `?usize`. Just because Linux uses 0 as end of stream doesn’t mean Zig has to do the same. Consider an asynchronous reader. If there’s some data ready to read, it reads it. If there isn’t, we would like it return immediately, in which case it should return 0. But this is not allowed by the interface now. This reader would be forced to block and wait for at least one byte to be ready, before returning.

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [April 26, 2025, 5:03pm UTC](https://ziggit.dev/t/should-read-return-usize/9827/5 "2025-04-26T17:03:36Z")

</div>

I’ve found it useful to distinguish between `0`, meaning “polling succeeded and nothing was read”, and `null`, meaning “polling did not find anything ready to read from”.

Not sure what anyone should conclude from that, but it seemed relevant.

---

<div class="post-metadata">

**Author:** ![g41797](https://ziggit.dev/user_avatar/ziggit.dev/g41797/32/3339_2.png) [@g41797](https://ziggit.dev/u/g41797)\
**Post date:** [April 26, 2025, 6:13pm UTC](https://ziggit.dev/t/should-read-return-usize/9827/6 "2025-04-26T18:13:36Z")

</div>

i don’t understand the difference - both are not errors

```zig
        /// Returns the number of bytes read. It may be less than buffer.len.
        /// If the number of bytes read is 0, it means end of stream.
        /// End of stream is not an error condition.
        pub fn read(self: *Self, buffer: []u8) Error!usize {

```

for low-level language it’s better to be close to C/OS API

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [April 26, 2025, 6:20pm UTC](https://ziggit.dev/t/should-read-return-usize/9827/7 "2025-04-26T18:20:14Z")

</div>

The difference lies in the difference between reading (which blocks) and polling (which always returns).

When polling, it’s useful and important to distinguish between “this has finished reading” and “this did not happen to read”.

---

<div class="post-metadata">

**Author:** ![g41797](https://ziggit.dev/user_avatar/ziggit.dev/g41797/32/3339_2.png) [@g41797](https://ziggit.dev/u/g41797)\
**Post date:** [April 26, 2025, 6:28pm UTC](https://ziggit.dev/t/should-read-return-usize/9827/8 "2025-04-26T18:28:45Z")

</div>

still the question is “are we going to provide interface similar to C/OS or provide new one”

---

<div class="post-metadata">

**Author:** ![LucasSantos91](https://ziggit.dev/user_avatar/ziggit.dev/lucassantos91/32/440_2.png) [@LucasSantos91](https://ziggit.dev/u/LucasSantos91)\
**Post date:** [April 26, 2025, 6:51pm UTC](https://ziggit.dev/t/should-read-return-usize/9827/9 "2025-04-26T18:51:47Z")

</div>

It already is a new interface. The OS doens’t have the notion of zig errors. I see no value in mimicking the OS.

---

<div class="post-metadata">

**Author:** ![mnemnion](https://ziggit.dev/user_avatar/ziggit.dev/mnemnion/32/2478_2.png) [@mnemnion](https://ziggit.dev/u/mnemnion)\
**Post date:** [April 26, 2025, 7:28pm UTC](https://ziggit.dev/t/should-read-return-usize/9827/10 "2025-04-26T19:28:28Z")

</div>

Andrew is also refactoring I/O with the aim of producing a ‘colorblind’ interface which works the same way with blocking as with polling. I’m interested in what he comes up with, and it’s a safe bet that the part of the current Reader interface we’re discussing here will work differently.

I use `null` to represent ‘not ready on poll’, but that decision was downstream of readers returning `0` when there’s nothing left to read. If they returned `null` I’d have done something else.

I don’t think `0` is much of a ‘magic number’ here, `(bytes_read == 0)` is a fairly coherent expression. I grant the point that control flow would be nicer with a `null` however.
