Repository navigation
Add sugar v[a .. b] be sugar for vec.slice and str.slice #4160
Description
Activity
What about having some sort of sugar for
str.len()? D allows you to use$in index and slice operators to specify the length of the arrays. Eg:// These expressions are equivalent: bar[] bar[0 .. 4] bar[0 .. $] bar[0 .. bar.length]Also (from this article):
int[] c = a[$-2..$]; // c refers to the last two elements of a // ($ stands for length inside a slice or index operation).D also allows you to overload the slice and slice assignment operators. The nice thing about being able to overload those operators would be that you'd be able to implement vector slicing in core, as opposed to it being some compiler magic.
How about copying the python syntax (http://stackoverflow.com/questions/509211/good-primer-for-python-slice-notation).
I.e (in python):
a[start:end] # items start through end-1 a[start:] # items start through the rest of the array a[:end] # items from the beginning through end-1 a[:] # a copy of the whole arrayReacted by JanfelThat's a decent idea. So
startandendwould be new keywords? That's the only bummer.startandendare just variable names in the Python example. I think the suggestion is that if you don't give an index, it either stands for 0 or length() - 1.Oh, silly me. Yeah I see. That seems like an elegant solution.
+1 for copying the Python syntax.
Special interpretation of negative numbers scares me. It sounds both slow (requiring a branch) and dangerous (you expected a crash, but instead you got the last element of the array). I prefer D's approach.
I also prefer the D syntax using
... This is also the same syntax as in match range patterns. I don't think leaving off the first component (like in Pythonarr[:9]) makes much sense, as it can be simply written as a 0. Using$as the length also makes absolute sense to me. I am thinking of it as in a regular expression. Python uses a:because it has an additional step element, and I think Python always copies slices because of this.The issue with using
$is that it conflicts with macros.Does
#work?For the rare case that we would need
$in a macro, couldn't it be escaped in some way? What about this:a[0:] vec::view(a, 0, a.len()) a[0:3] vec::view(a, 0, 3) a[0:^1] vec::view(a, 0, a.len()-1)Here
^1would mean without one element at the end. This has the advantage that we don't have to do the minus as in$-1ourself and as such might prevent some casting issues (the end index could become negative). Similarily one could apply the same for the beginninga[^1:]would means "without the first element". The only advantage I would see here over plaina[1:]is that of non-zero indexed arrays (which we don't have of course).I also favor the python syntax, it comes very handy. Including using negative numbres to specify the offset from the end of an array. Does it really need a branch?
Prefer not to add $ or ^ support, nor a syntax different from ".." for ranges (which is currently supported in expression and pattern grammars, and may be worth using in type grammar rather than the current "* N"). 0 and foo.len() are ok for the explicit ends. Think simplicity and symmetry.
Keep in mind we now support restructuring vector patterns, which soaks up some of this feature.
I don't like using
foo.len()for the end because it's easy to mess up, and becausefoomight be a complicated expression I don't want to repeat.10 remaining items
Just for the record, I favor this strongly, and this is my preferred form.
- Add a
Slicetrait (see below). - Add a slice operator
expr[a..b]whereaandbare optional.- The operator autoderefences, like indexing.
- For fixed-length vectors, its effect is built-in.
- For other types, it is translated to a call to the appropriate trait method:
expr[..] => expr.as_slice()expr[a..] => expr.slice_from(a)expr[..b] => expr.slice_to(b)expr[a..b] => expr.slice(a, b)
- Do we have something for mutable slices? Perhaps
expr[mut a..b]? These come up less frequently but nonetheless it seems useful, particularly for fixed-length vectors since otherwise there remains no explicit syntax for slicing one that does not rely on coercion.
Note there is no special syntax for length. That eliminates some use cases. Oh well. There is also no accommodation for negative indices. Oh well. These things are not that important: the main use is
a[..-1]to lop off the last thing in the list, and we havehead()for that.trait Slice<T> { fn as_slice<'a>(&'a self) -> &'a [T]; ... } trait MutSlice<T> { ... }- Add a
I like @nikomatsakis proposal.
However, I think negative indices should be used like in Ruby, -1 would be the last element, so [-5..-1] would give the last 5 elements.
cc me
I like this proposal!
Hm, most of the time I am either dropping off elements from the front of a vector or from the tail of it, or more seldom from both sides at the same time. Without using negative indices, the slice operator is missing a common use-case (dropping off elements from the end). BUT, I don't like the use of negative numbers for counting backwards. Something like
expr.init()is IMHO much more verbose thanexpr[..-2]and much less prone to errors, except that I would preferexpr.without_lastorexpr.excluding_lastto be easier to understand, especially when it comes toinitnwhich is completely misleading IMHO as it does not return the initial n elements, NO, it excludes the last n elements.So my suggestion would be to not include the slice operator at all, but provide more understandable methods like
firstn,lastn,excluding_last,excluding_lastnetc.Extending indexing from elements
arr[i]to slicesarr[i..j]is straightforward and easy to grok. I agree about providing dedicated methods instead for the remaining use cases.+1 to nmatsakis' proposal verbatim. No comma, no syntax for length, no negative indices. Clean and simple.
Another +1 to nikomatsakis' proposal.
It's intuitive and doesn't preclude adding negative indexes in the future if there is a huge demand for them.Another +1 for Niko's proposal. It's well thought-out.
The one problem with @nikomatsakis's proposal is it requires contiguous storage, so e.g. doesn't work with rope types, or vectors with strides.
One more +1 for Niko's proposal.
I don't think the complexity is worth it really.
+1
2013年11月21日 上午1:21于 "Niko Matsakis" notifications@github.com写道:Just for the record, I favor this strongly, and this is my preferred form.
-
Add a Slice trait (see below).
-
Add a slice operator expr[a..b] where a and b are optional.
- The operator autoderefences, like indexing.
- For fixed-length vectors, its effect is built-in.
- For other types, it is translated to a call to the appropriate
trait method:
- expr[..] => expr.as_slice() - expr[a..] => expr.slice_from(a) - expr[..b] => expr.slice_to(b) - expr[a..b] => expr.slice(a, b) 3. Do we have something for mutable slices? Perhaps expr[mut a..b]?These come up less frequently but nonetheless it seems useful, particularly
for fixed-length vectors since otherwise there remains no explicit syntax
for slicing one that does not rely on coercion.
Note there is no special syntax for length. That eliminates some use
cases. Oh well. There is also no accommodation for negative indices. Oh
well. These things are not that important: the main use is a[..-1] to lop
off the last thing in the list, and we have head() for that.trait Slice<T> { fn as_slice<'a>(&'a self) -> &'a [T]; ... } trait MutSlice<T> { ... }—
Reply to this email directly or view it on GitHubhttps://github.com//issues/4160#issuecomment-28909198
.-
Clearly this is RFC material. I'm going to close this issue and open one on the rfcs repo :)
- added a commit that references this issue
on Feb 2, 2025
** UPDATE by @nikomatsakis **
Closed in favor of this RFC repo issue: rust-lang/rfcs#13
** ORIGINAL **
@graydon said in irc today: