Erroneous `local variable is never mutated` error when using inferred many-item pointer
ssmid opened this issue · comments
Zig Version
0.14.0-dev.121+ab4c461b7
Steps to Reproduce and Observed Behavior
const std = @import("std");
fn process(iovs: [*]std.posix.iovec, len: usize) void {
for (0..len) |i| {
iovs[i].len += 1;
}
}
pub fn main() void {
var buffer: [1024]u8 = undefined;
const iov = std.posix.iovec{
.base = &buffer,
.len = buffer.len,
};
process(&.{iov}, 1);
std.debug.print("{d}\n", .{iov.len});
}
Output:
test.zig:12:6: error: local variable is never mutated
var iov = std.posix.iovec{
^~~
test.zig:12:6: note: consider using 'const'
However, when using const
:
test.zig:16:10: error: expected type '[*]posix.iovec', found '*const [1]posix.iovec'
process(&.{iov}, 1);
^~~~~~~
test.zig:16:10: note: cast discards const qualifier
test.zig:4:19: note: parameter type declared here
fn process(iovs: [*]std.posix.iovec, len: usize) void {
~^~~~~~~~~~~~~~~~~
referenced by:
posixCallMainAndExit: ~/bin/zig-0.14.0.121+ab4c461b7/lib/std/start.zig:505:17
_start: ~/bin/zig-0.14.0.121+ab4c461b7/lib/std/start.zig:342:40
remaining reference traces hidden; use '-freference-trace' to see all reference traces
Naturally happens when trying to use (posix) sendmsg().
Expected Behavior
Not fail, as iov in fact is mutated.
Not a bug. &.{iov}
is not getting a pointer to iov
, but rather constructing a new temporary array on the stack (which, as with all temporaries, is const
) and taking a pointer to it.
You want to take a pointer to iov
and convert it to a many-ptr. Try (&iov)[0..1]
.
Your solution does work, but I'm wondering a bit because using slice syntax on a single-item pointer is not in the documentation and also seems a bit off.
It was added semi-recently (I think it's been in since 0.12.0 but I might be wrong). Slicing a *T
from 0..1
converts it to a *[1]T
, which can coerce to [*]T
. You could equivalently use @ptrCast
, but it's better to use the slicing syntax since this conversion is safe.
If the langref doesn't explain single-item pointer slicing (which wouldn't surprise me, although I can't check right now), feel free to open a PR to explain it!
Just found the relevant issue proposals: #3156 #16075 #8197
This is the current text in the langref:
Zig has two kinds of pointers: single-item and many-item.
*T - single-item pointer to exactly one item.
Supports deref syntax: ptr.*
Supports pointer subtraction: ptr - ptr
[*]T - many-item pointer to unknown number of items.
[...]
I will make a PR soon.
fwiw I would write the following as I find it more readable:
var iov = [_]std.posix.iovec{.{
.base = &buffer,
.len = buffer.len,
}};
process(&iov, 1);
That's also a very fair point -- looking at this code properly, I'd probably also write that.