-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Description
Background
The slicing operator in Zig, a[b..c]
, expects a
to be either a slice(*) or an array, and returns a slice(*) of the elements between index b
and c
. That's all well and good... except for one thing. If a
can be an array, then this syntax form needs to take its address (i.e. &a
) in order to return a slice! Of course, that's not necessary if a
is a slice, but we need to decide how we're evaluating a
before we learn its type, so we need to assume the worst and always take the implicit reference, then dereference that pointer again if it turned out to already be a slice (which is the far more common case!).
Taking addresses unnecessarily is generally undesirable. The main reason for this is that it harms our heuristic for "local variable is never mutated" compile errors -- for instance, the following snippet does not emit a compile error:
test {
// incorrect use of 'var': we don't mutate the *slice*!
var buf = try std.testing.allocator.alloc(u8, 10);
defer std.testing.allocator.free(buf);
// the compiler sees `buf[5..]`, and realises that if `buf` is an array, this code
// actually will mutate it. therefore, it cannot emit the compile error, even though
// in reality, `buf` is a slice, not an array.
const part = buf[5..];
part[0] = 123;
}
const std = @import("std");
(*): or pointer-to-array, which by design acts almost identically to a slice
Proposal
Remove the ability to use slicing syntax (a[b..]
, a[b..c]
, a[b.. :d]
, a[b..c :d]
) to get a slice from an array.
The breakages of user code are summarized by this theoretical diff:
test {
var arr: [10]u8 = undefined;
// using slicing to actually get a sub-slice of the array
- @memcpy(arr[0..5], "hello");
+ @memcpy((&arr)[0..5], "hello");
// using slicing just to turn an array into a slice
- otherStuff(arr[0..]);
+ otherStuff(&arr);
// unnecessary use of 'var'
- var ptr = &arr;
+ const ptr = &arr;
@memset(ptr[5..], 0);
}
(To be clear, that third example is just a slight variant of the "unnecessary use of var
" snippet I showed earlier; that earlier example would also need changing.)
The first change above is perhaps mildly annoying, but I would argue improves clarity, particularly in situations where double-pointers might be involved. The second is an improvement because it gives one obvious way to do array->slice conversion (whereas today, some people do still write arr[0..]
for that). The third is an obvious improvement.
All three of the previously-working lines in the above snippet will fail with compile errors. In order:
error: cannot slice array value
note: use '&' to convert array to slice
error: cannot slice array value
note: use '&' to convert array to slice
error: local variable is never mutated
note: consider using 'const'