#Why can't I take a slice of a single item pointer?
1 messages · Page 1 of 1 (latest)
because single item pointers aren't slices?
if you're working with pointers to multiple items, you should be using slices in the first place
that's the point of having multiple pointer types
The real world case is where one is calling some library API that takes a slice but the specific usage only ever passes a single thing. Multi-item pointers coerce, why not single-item pointers?
what do you mean multi-item pointers coerce? Do you mean array pointers?
yes
Multipointers don't coerce to slices AFAIK, because they don't know what length to slice at - so you have to do mptr[0..n] to do that.
*[n]T coerces to []T because it is an obvious and often-encountered conversion. slice.len = array_ptr.len, slice.ptr = array_ptr
single item pointers don't coerce because they are not something that is considered to point to multiple items
yeah that's what i'm saying the ptr[0..n] syntax doesn't work for single item pointer, but why is single_ptr[0..1] not legal?
because it doesn't make sense to slice a single item
that's just the design of the type system
but its quite common when calling api that work with slices
yes, which is non-obvious and friction. is that friction necessary?
yes, because single item pointers are not usually meant to be treated as pointers to many items. A pointer to one item isn't supposed to point to many items, and it doesn't make sense to treat it as if it were able to, only to solve that one use case, which is already solved by typing a few extra characters
I would also argue it's about as non-obvious as any other zig-specific feature, but makes sense within the context of zig as it is
that you could slice a single item pointer makes no sense within the context of zig
it makes sense in the context of writing code. this is something that people bump into and @as(*[1]T, single_ptr) is only obvious once you've seen it. there are other, less nice and footgunnable workarounds too. at the very least, the compiler should offer the correct workaround as advice when giving the single-item pointer error. but i'm really struggling to see what would be so bad about accepting the coercion when the slice length is compile-time known
sure, error messages are always improvable.
but it would be bad, because it creates an ambiguity in the type system. Currently, *[n]T coerces to []T. But if *T can coerce to []T directly, then the logic/reasoning behind the coercion between all of these becomes confusing. Does this mean that *[n]T should also be coercible to [][n]T? What about *T to [*]T, since *[1]T can also coerce to that? That certainly doesn't sound good.
It's just more simple as it is, there is virtually no room for emergent behaviour, whereas there is with the suggested coercion.
I don't see any value in allowing it, and can only imagine emergent degradation of the type system as a result of it
thanks, this is what i was looking for
@neat flume thanks for the answer. I need to think about it a bit. trying to figure out where &myvalue[0..1] could be ambiguous.
one edge case example of where it may be problematic is actually userland "type inference" code. Right now, it's pretty simple for a function to take slice: anytype, and infer that if the function passes *[n]T, then it should be treated like []T. If the suggested coercion were allowed, then it would no longer be clear if what the code should do is the aforementioned conversion, or instead treat it like [][n]T; as it is, you have to explicitly opt in to treating a pointer to your single value as a pointer to potentially many values
this could be worked around of course
but that's worse than status quo
hmmm... a bit busy now but will think more about this later, it feels like it ought to work unambiguously! 😄
"as it is, you have to explicitly opt in to treating a pointer to your single value as a pointer to potentially many values"
is using the [0..] syntax not sufficient to explicitly opt in?
bump Q:
is using the [0..] syntax not sufficient to explicitly opt in?
I mean, that would be a way to explicitly opt-in, but it would be weird to be able to do that on single item pointer
why is it weird? we're just saying that we want to treat this as a collection of one right?
That isn't really what "slicing" means
Generally slicing is an operation which returns a pointer to a subsection of a span of memory
And usually, it's a subsection containing 0, 1, or more items
A single item pointer doesn't have a "section" as it were, it just points to one object
That's the point of having a type system with multiple pointer types
Otherwise we could just get rid of single item pointers and allow doing .* on many item pointers
im not saying we should change the type system or define the meaning of things differently, just that is seems like there is a non-destructive way to reduce friction in this one specific case that is within the bounds of the type system as-it-is
what is a "section"? everything is just spans of memory really
well, everything is just bytes, but I'm not talking about representation, I mean in the context of the type system
at any rate, I just don't agree that that's the way to go about it
I should be able to read code, and know that if I see x[0..], that x is a pointer to 0, 1, or more items, and not have to guess whether it's that, or a single item pointer
gotcha. it seems odd that this is controversial to me.
i don't understand this reasoning tbh but i guess we're not going to agree here
i too like to be able to read code and know what is going on. but this just makes no sense to me in this case unfortunately
and, with respect, the goal posts keep moving:
first it was "why are you even doing that"
then it was "oh ok, yes but there would be ambiguity"
and then "oh ok that is explicit, but i don't like it"
empahasis on the "with respect" bit, sincerly not trying to be an arsehole
With respect in return, I'm not moving any goal posts, I'm responding to different points.
First it was that you wanted to treat a single item pointer as a slice, which isn't the general case.
Then you brought up a specific use case for implicit coercion, and I said it would be ambiguous.
And then you proposed to use a different syntax to remove the ambiguity, and I said I don't think it makes sense/I don't like it.
and dw, feel free to point out whenever you feel someone is being disingenuous/acting incoherently
i dunno man, it's been thing_ptr[0..] since the beginning
but thanks for taking the time to answer
do appreciate it
yeah, as has been @as(*[1]T, ptr). I think that makes more sense in the type system than allowing a slice on a single item pointer
again, sincerly, hard to not sound sarcastic in text form
textual communication makes it difficult to parse tone, so don't worry
you've done nothing to indicate to me that you are a sarcastic or malintentioned actor, so I give you the benefit of the doubt by default
I enjoyed the discussion
it's good to argue about these things
it made me think about my position on the topic, and I feel more confident in it now after having had to defend it
👍 just pushing on things to see where we can reduce friction for user without compromising type-safety and readabilty
Note that there's 2 potential proposals for this:
https://github.com/ziglang/zig/issues/8197
https://github.com/ziglang/zig/issues/3156
Neither have been accepted or rejected yet
It's actually interesting because the idea that "everything is just a bunch of bits" isn't necessarily antithetical to "have a strong type system".
I tend to like the latter because it helps guard against making mistakes, but it's also not enough to prevent the majority of mistakes you actually make: logic bugs.
While having less of a type system obviously won't help with that, the idea that everything is ultimately just a bunch of bits that you are manipulating doesn't necessarily mean that.
It does interest me as a topic, for a similar reason that DoD does; them seem somewhat related to me.
As such, I fancy there may be some interesting ideas that could be made manifest there.
can anyone help me with the above PR?
this is what i'm doing
const ptr = try sema.analyzeLoad(block, src, ptr_ptr, ptr_src);
if (sema.typeOf(ptr).ptrSize() == .One) {
const msg = msg: {
const msg = try sema.errMsg(block, src, "slice of single-item pointer", .{});
errdefer msg.destroy(sema.gpa);
try sema.errNote(
block,
src,
msg,
"perhaps you want to coerce using '@as(*[1]{}, ptr)'?",
.{ptr_ptr_child_ty.childType().fmt(mod)},
);
break :msg msg;
};
return sema.failWithOwnedErrorMsg(msg);
} else {
return sema.fail(block, src, "slice of single-item pointer", .{});
}
but the condition is incorrect. How do I extract the "requested slice length"?
isnt a single item pointer a "section" containing 1 item
whats the difference?
you treat a pointer to one item as a single value that you can operate on. A slice of items is a pointer to many items that can each be operated on, wherein a slice of 1 items just happens to have the same representation as a single item pointer
the difference lies in how you operate with them?
yeah, that's the difference between literally all types
everything is just bytes
types are just an abstraction over them
pointers have properties that integers dont, types have different alignment/access-modifiers/etc from others
what in-memory properties do pointers have that integers don't?
provenance - constness, what range of bytes its allowed to access, if its being accessed in the case of TSAN, hidden hardware verification in the case of CHERI
those boil down to how you encode the values
at the end of the day, you can move one thing into a register, and ask the CPU to operate on it however you want, and then suffer the consequences
in that case, the difference is how the hardware operates on them vs the source
but at the source level:
- there are different types that you operate on similarly: u32 vs f64 for example
- there are also different types that the compiler operates on differently in which the hardware operates on the same way (volatile/atomics)
but going back to ptr vs slice, their main diff being how u operate on them makes sense. What are u thoughts about implicit coercion then? (currently *[N]T to []T)
sure, but my argument is that it's worth distinguishing single item pointers from many item pointers, because they represent different purposes, vs numerical operations and instruction observability/atomicity.
The coercion from *[n]T to []T makes sense because they are both pointing to multiple items, and it makes pragmatic sense to allow it given the fact that if it were not present, you couldn't assign the result of a slicing operation with comptime indices back to a slice.
The coercion from *T to *[1]T makes sense as a convenience for the use cases specified in this thread.
The proposed implicit coercion from *T to []T doesn't make sense, because the former doesn't point to many items, and can't point to 0 items, it only ever point to one item.
but the proposal is actually an explict coercion from *T to *[1]T that implicitly coerces to []T
if it were not present, you couldn't assign the result of a slicing operation with comptime indices back to a slice.
slicing with comptime indices still gives a slice no?
no, @TypeOf(x[a..b]) = *[b - a]T when a & b are comptime-known
sure, it's less egregious, but I just still don't think it makes sense
especially when the coercion already exists
right, the coecion already exists, so we're really only talking about allowing slice syntax to represent that coersion instead of the nasty @as thing
me yesterday 😄
makes sense given x[a..b].* gives an array back
is there a better place to ask for help on this?
prob #compiler-devel
thanks
zero response in stage 2 meetings. straightup just ignored 🙂
so also asking again but here: https://discord.com/channels/605571803288698900/1076664356588228638