digitalmars.D.learn - The difference between T[] opIndex() and T[] opSlice()
- Salih Dincer (23/23) Oct 01 2023 Hi,
- Salih Dincer (6/8) Oct 01 2023 [Here](https://dlang.org/spec/operatoroverloading.html#slice),
- Jonathan M Davis (25/34) Oct 01 2023 I suspect that the only people who really understand the full mess with
- Salih Dincer (59/67) Oct 01 2023 Overloading has nothing to do with indexing, so I'll use opSlice.
- Steven Schveighoffer (4/8) Oct 01 2023 None. It used to be that opSlice was the only way, and the mechanisms
- Paul Backus (3/6) Oct 02 2023 `T[] opSlice()` is the D1 version and exists only for backwards
- Salih Dincer (25/31) Oct 02 2023 Forgive me for asking again, I think opSliceAssign(T value) has
- Paul Backus (17/26) Oct 02 2023 I don't know what's wrong in your example but this works for me:
- Salih Dincer (4/5) Oct 02 2023 I found the reason for the error:
- Steven Schveighoffer (18/46) Oct 03 2023 So in the example linked by Salih, the `opIndex` returns a ref,
- Paul Backus (8/12) Oct 03 2023 Works for me, with both `++` and `+=`:
- Steven Schveighoffer (5/19) Oct 03 2023 OK, so it's not as bad as I thought, but surely the compiler should
- Paul Backus (8/11) Oct 03 2023 Maybe. On the other hand, if you make a typo in the body of your
- Imperatorn (2/15) Oct 03 2023 Who will be the hero and add the documentation? 😇
- Salih Dincer (28/32) Oct 03 2023 More importantly, is there a priority order? Because in our last
- Paul Backus (10/15) Oct 03 2023 Source: https://dlang.org/spec/operatoroverloading.html#array
- Gaurav Negi (27/27) Oct 05 2023 Well, in the D programming language, both opIndex and opSlice are
- Salih Dincer (63/66) Oct 05 2023 Yeah, D is on its way to becoming a near-perfect programming
Hi, What is the difference between T[] opIndex() and T[] opSlice(), which haven't parameters? ```d struct S(T) { T[] arr; T[] opIndex() => arr[];/* T[] opSlice() => arr;//*/ } alias Type = int; void main() { auto s = S!Type([1,2,3]); auto arr = s[]; // calls s.opIndex() assert(arr == [1,2,3]); assert(is(typeof(arr): Type[])); } ``` Also, is it correct to use [] when returning? Thanks...
Oct 01 2023
On Sunday, 1 October 2023 at 17:41:08 UTC, Salih Dincer wrote:Also, is it correct to use [] when returning? Thanks...[Here](https://dlang.org/spec/operatoroverloading.html#slice), the opIndex() is proposed and an example of parameterized the opSlice() is given for multidimensional arrays. Like there's no difference, huh? Puff :)
Oct 01 2023
On Sunday, October 1, 2023 11:51:17 AM MDT Salih Dincer via Digitalmars-d- learn wrote:On Sunday, 1 October 2023 at 17:41:08 UTC, Salih Dincer wrote:I suspect that the only people who really understand the full mess with opIndex and opSlice at this point are the folks who have done a bunch with multi-dimensional containers, which fortunately, I haven't had to deal with any time recently, so I'm not well-versed on all of the nitty-gritty details. The situation used to be a bit clearer, but folks wanted better support for multi-dimensional containers, so some changes were made. The result is that if you're dealing with multiple arguments, the difference in which is called should come down to whether you're using the slice operator between indices - .. - or whether you're using commas (though since you can mix and match to an extent with multi-dimensional containers, it's still pretty confusing IMHO). Either way, when you have no arguments, the situation is certainly more confusing than it used to be. Previously, you would have always used opSlice with no parameters for something like returning the full slice of a container, but I think that it's now possible to do exactly the same thing with opIndex (confusing as that may be), because that made some sense when writing a variadic opIndex. For most code, you'd just write an opIndex with a single parameter for indexing an element, opSlice with two parameters for slicing the range or container, and then either opIndex or opSlice with no parameters to return a slice of the entire container (in which case, personally, I'd use opSlice, because semantically, that's what you're doing, but either should work IIRC). - Jonathan M DavisAlso, is it correct to use [] when returning? Thanks...[Here](https://dlang.org/spec/operatoroverloading.html#slice), the opIndex() is proposed and an example of parameterized the opSlice() is given for multidimensional arrays. Like there's no difference, huh? Puff :)
Oct 01 2023
On Monday, 2 October 2023 at 02:01:34 UTC, Jonathan M Davis wrote:For most code, you'd just write an opIndex with a single parameter for indexing an element, opSlice with two parameters for slicing the range or container, and then either opIndex or opSlice with no parameters to return a slice of the entire container (in which case, personally, I'd use opSlice, because semantically, that's what you're doing, but either should work IIRC).Overloading has nothing to do with indexing, so I'll use opSlice. ```d import std.stdio; import std.range; struct Matrix(T) { private T[][] elements; size_t length; T* ptr; this(size_t length) { this.length = length * length; size_t m = T.sizeof * this.length; ubyte[] arr = new ubyte[](m); ptr = cast(T*)arr.ptr; m /= length; foreach(i; 0 .. length) { size_t n = i * m; elements ~= cast(T[])arr[n .. n + m]; } } ref T opIndex(size_t i) in(i < length) => ptr[i]; auto opDollar() => length; auto opSliceAssign(T value, size_t a, size_t b) in(a <= length && b <= length) => ptr[a..b] = value; auto opSlice() => elements; auto opSliceAssign(T value) { foreach(i; 0 .. length) { ptr[i] = value; } } } void main() { auto arr = Matrix!double(3); size_t n; foreach(value; iota(0.1, 1, 0.1)) arr[n++] = value; arr[].writefln!"%-(%-(%s %)\n%)\n"; arr[0..$/2] = 0; // reset a slice arr[].writefln!"%-(%-(%s %)\n%)\n"; arr[] = 0; // reset all arr[].writefln!"%-(%-(%s %)\n%)\n"; arr[6..9] = 1; // set a slice arr[].writefln!"%-(%-(%s %)\n%)\n"; } ``` SDB 79
Oct 01 2023
On 10/1/23 1:41 PM, Salih Dincer wrote:Hi, What is the difference between T[] opIndex() and T[] opSlice(), which haven't parameters?None. It used to be that opSlice was the only way, and the mechanisms opSlice uses are still valid. -Steve
Oct 01 2023
On Sunday, 1 October 2023 at 17:41:08 UTC, Salih Dincer wrote:Hi, What is the difference between T[] opIndex() and T[] opSlice(), which haven't parameters?`T[] opSlice()` is the D1 version and exists only for backwards compatibility. You should use `T[] opIndex()` in new code.
Oct 02 2023
On Monday, 2 October 2023 at 16:05:39 UTC, Paul Backus wrote:`T[] opSlice()` is the D1 version and exists only for backwards compatibility. You should use `T[] opIndex()` in new code.Forgive me for asking again, I think opSliceAssign(T value) has also been improved, right? ```d // ... auto opSlice() { return elements[]; } auto opSliceAssign(T value) { foreach(i; 0 .. length) { ptr[i] = value; } } } ``` In an old version (for example, v2.0.83), the code you implemented in the places where Slice is written above works as desired. In the most current versions, the parameterized opIndexAssign(T value) gives the error:onlineapp.d(51): Error: function `onlineapp.Matrix!double.Matrix.opIndexAssign(double value)` is not callable using argument types `(double, ulong)` onlineapp.d(51): expected 1 argument(s), not 2**Source:** https://run.dlang.io/is/TPAg5m Thanks... SDB 79
Oct 02 2023
On Monday, 2 October 2023 at 20:34:11 UTC, Salih Dincer wrote:In an old version (for example, v2.0.83), the code you implemented in the places where Slice is written above works as desired. In the most current versions, the parameterized opIndexAssign(T value) gives the error:I don't know what's wrong in your example but this works for me: ```d struct S { void opIndexAssign(int value) { import std.stdio; writeln("assigned ", value); } } void main() { S s; s[] = 7; } ```onlineapp.d(51): Error: function `onlineapp.Matrix!double.Matrix.opIndexAssign(double value)` is not callable using argument types `(double, ulong)` onlineapp.d(51): expected 1 argument(s), not 2**Source:** https://run.dlang.io/is/TPAg5m
Oct 02 2023
On Monday, 2 October 2023 at 20:42:14 UTC, Paul Backus wrote:I don't know what's wrong in your example but this works for me:I found the reason for the error: https://forum.dlang.org/thread/vckvftkdzcrnikuduuqp forum.dlang.org SDB 79
Oct 02 2023
On Monday, 2 October 2023 at 20:42:14 UTC, Paul Backus wrote:On Monday, 2 October 2023 at 20:34:11 UTC, Salih Dincer wrote:So in the example linked by Salih, the `opIndex` returns a ref, which is a valid mechanism to properly do `a[0] = val;`. However, since `opIndexAssign` exists, the compiler expects that to be used instead. Essentially, by naming the slice assign the same operator as index assign, you have eliminated the possibility for ref assignment via indexing. Now, you can define a further `opIndexAssign(T val, size_t idx)`. However, now you lose capabilities like `a[0]++`, which I don't think has a possibility of implementing using an `opIndex` operator, and it would be pretty ugly if you had to. This seems like a design flaw in the `opIndex` overloading changes. I would stick with `opSliceAssign` if faced with this problem (glad it still works!) It could also be considered a bug but I don't know the overload implications. -SteveIn an old version (for example, v2.0.83), the code you implemented in the places where Slice is written above works as desired. In the most current versions, the parameterized opIndexAssign(T value) gives the error:I don't know what's wrong in your example but this works for me: ```d struct S { void opIndexAssign(int value) { import std.stdio; writeln("assigned ", value); } } void main() { S s; s[] = 7; } ```onlineapp.d(51): Error: function `onlineapp.Matrix!double.Matrix.opIndexAssign(double value)` is not callable using argument types `(double, ulong)` onlineapp.d(51): expected 1 argument(s), not 2**Source:** https://run.dlang.io/is/TPAg5m
Oct 03 2023
On Tuesday, 3 October 2023 at 13:07:00 UTC, Steven Schveighoffer wrote:Now, you can define a further `opIndexAssign(T val, size_t idx)`. However, now you lose capabilities like `a[0]++`, which I don't think has a possibility of implementing using an `opIndex` operator, and it would be pretty ugly if you had to.Works for me, with both `++` and `+=`: https://run.dlang.io/is/JckTVG AST output confirms that these are lowered to use `opIndex`. Looking at the spec, it seems like `opIndex` would only be pre-empted here if you overloaded `opIndexUnary` (for `++`) and/or `opIndexOpAssign` (for `+=`).
Oct 03 2023
On 10/3/23 12:09 PM, Paul Backus wrote:On Tuesday, 3 October 2023 at 13:07:00 UTC, Steven Schveighoffer wrote:OK, so it's not as bad as I thought, but surely the compiler should recognize that `opIndexAssign(val, idx)` doesn't work, but `opIndex(idx) = val` does? -SteveNow, you can define a further `opIndexAssign(T val, size_t idx)`. However, now you lose capabilities like `a[0]++`, which I don't think has a possibility of implementing using an `opIndex` operator, and it would be pretty ugly if you had to.Works for me, with both `++` and `+=`: https://run.dlang.io/is/JckTVG AST output confirms that these are lowered to use `opIndex`. Looking at the spec, it seems like `opIndex` would only be pre-empted here if you overloaded `opIndexUnary` (for `++`) and/or `opIndexOpAssign` (for `+=`).
Oct 03 2023
On Tuesday, 3 October 2023 at 16:45:39 UTC, Steven Schveighoffer wrote:OK, so it's not as bad as I thought, but surely the compiler should recognize that `opIndexAssign(val, idx)` doesn't work, but `opIndex(idx) = val` does?Maybe. On the other hand, if you make a typo in the body of your templated `opIndexAssign` overload, do you want the compiler to silently fall back to `opIndex`, or do you want an error? There are pros and cons to both approaches. At the very least, the spec should do a better job of documenting when the compiler will try a fallback and when it won't.
Oct 03 2023
On Tuesday, 3 October 2023 at 17:52:20 UTC, Paul Backus wrote:On Tuesday, 3 October 2023 at 16:45:39 UTC, Steven Schveighoffer wrote:Who will be the hero and add the documentation? 😇OK, so it's not as bad as I thought, but surely the compiler should recognize that `opIndexAssign(val, idx)` doesn't work, but `opIndex(idx) = val` does?Maybe. On the other hand, if you make a typo in the body of your templated `opIndexAssign` overload, do you want the compiler to silently fall back to `opIndex`, or do you want an error? There are pros and cons to both approaches. At the very least, the spec should do a better job of documenting when the compiler will try a fallback and when it won't.
Oct 03 2023
On Tuesday, 3 October 2023 at 18:09:55 UTC, Imperatorn wrote:More importantly, is there a priority order? Because in our last example, when we leave a single overload, all features are executed through the ref opIndex except the bit: ```d struct S { int[] i; ref opIndex(size_t index) => i[index]; //void opIndexAssign(int value) { i[] = value; } //void opIndexAssign(int value, size_t idx) { i[idx] = value; } } void main() { auto s = S([2, 2]); s[0] = 2; assert(s.i == [2, 2]); s[1] = 42; assert(s.i == [2, 42]); s[0]++; assert(s.i == [3, 42]); s[0] += 1; assert(s.i == [4, 42]); } ``` So the important thing is: Who is dominant when another overload comes into play? SDB 79At the very least, the spec should do a better job of documenting when the compiler will try a fallback and when it won't.Who will be the hero and add the documentation? 😇
Oct 03 2023
On Tuesday, 3 October 2023 at 18:29:49 UTC, Salih Dincer wrote:More importantly, is there a priority order? Because in our last example, when we leave a single overload, all features are executed through the ref opIndex except the bit:The spec says:If an index expression can be rewritten using `opIndexAssign` or `opIndexOpAssign`, those are preferred over `opIndex`.Source: https://dlang.org/spec/operatoroverloading.html#array So, the more specialized overloads are tried first, and the more general `opIndex` is tried last. The only thing that's unclear here is the meaning of "can be rewritten"--as we've seen, the compiler only checks whether the specialized function *exists*, not whether it actually *works*, and will sometimes perform rewrites even when the resulting code does not compile.
Oct 03 2023
Well, in the D programming language, both opIndex and opSlice are two different operators used to access elements of a custom type. struct S(T) { T[] arr; T opIndex(size_t index) const { assert(index < arr.length, "Index out of range"); return arr[index]; } T[] opSlice(size_t startIndex, size_t endIndex) const { assert(startIndex <= endIndex && endIndex <= arr.length, "Invalid slice indices"); return arr[startIndex..endIndex]; } } alias Type = int; void main() { auto s = S!Type([1, 2, 3]); auto element = s[0]; // Calls s.opIndex() assert(element == 1); auto slice = s[1..3]; // Calls s.opSlice() assert(slice == [2, 3]); } Thanks
Oct 05 2023
On Thursday, 5 October 2023 at 16:40:49 UTC, Gaurav Negi wrote:Well, in the D programming language, both opIndex and opSlice are two different operators used to access elements of a custom type.Yeah, D is on its way to becoming a near-perfect programming language... ```d enum initialSituation = [1, 2, 3]; import std.stdio; void main() { auto s = S(initialSituation); s[] = 1; s[1] = 2; s[2] += 2; assert(s.arr == initialSituation); auto d = s *= 2; assert(d == S([2, 4, 6])); } /* Prints: [1, 1, 1]: onlineapp.S.opSliceAssign [1, 2, 1]: onlineapp.S.opIndexAssign [1, 2, 1]: onlineapp.S.opIndex [2, 4, 6]: onlineapp.S.opOpAssign!"*".opOpAssign */ struct S { int[] arr; auto opSliceAssign (int value) { scope(exit) { writefln("%s: %s", arr, __FUNCTION__); } return arr[] = value; } auto opIndexAssign(int value, int index) { scope(exit) { writefln("%s: %s", arr, __FUNCTION__); } arr[index] = value; } ref opIndex(size_t index) { scope(exit) { writefln("%s: %s", arr, __FUNCTION__); } return arr[index]; } ref opOpAssign(string op)(int value) { scope(exit) { writefln("%s: %s", arr, __FUNCTION__); } mixin("arr[] " ~ op ~ "=value;"); return this; } } ``` SDB 79
Oct 05 2023