Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sugar v[a .. b] be sugar for vec.slice and str.slice #4160

Closed
erickt opened this issue Dec 11, 2012 · 38 comments
Closed

Add sugar v[a .. b] be sugar for vec.slice and str.slice #4160

erickt opened this issue Dec 11, 2012 · 38 comments
Labels
C-enhancement Category: An issue proposing an enhancement or a PR with one.

Comments

@erickt
Copy link
Contributor

erickt commented Dec 11, 2012

** UPDATE by @nikomatsakis **

Closed in favor of this RFC repo issue: rust-lang/rfcs#13

** ORIGINAL **

@graydon said in irc today:

graydon: I would like foo[a..b] to work for slicing, tbh
graydon: I mean, if the square brackets (arguably some of the nicest symbols available in ascii) are stolen for vecs, we might as well use them as thoroughly as we can
@brendanzab
Copy link
Member

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.

@Dretch
Copy link
Contributor

Dretch commented Dec 13, 2012

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 array

@brendanzab
Copy link
Member

That's a decent idea. So start and end would be new keywords? That's the only bummer.

@kud1ing
Copy link

kud1ing commented Dec 14, 2012

start and end are 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.

@brendanzab
Copy link
Member

Oh, silly me. Yeah I see. That seems like an elegant solution.

@jruderman
Copy link
Contributor

+1 for copying the Python syntax.

@ghost
Copy link

ghost commented Jan 27, 2013

+1 on the Python syntax (including negative indices to specify offsets from the end of the vector). It would also be nice to allow the operator to be overloaded (e.g., trait Slice<From, To, Result>) for things like sorted sets.

@jruderman
Copy link
Contributor

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.

@mneumann
Copy link
Contributor

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 Python arr[: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.

@brendanzab
Copy link
Member

The issue with using $ is that it conflicts with macros.

@sanxiyn
Copy link
Member

sanxiyn commented Jan 28, 2013

Does # work?

@mneumann
Copy link
Contributor

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 ^1 would mean without one element at the end. This has the advantage that we don't have to do the minus as in $-1 ourself and as such might prevent some casting issues (the end index could become negative). Similarily one could apply the same for the beginning a[^1:] would means "without the first element". The only advantage I would see here over plain a[1:] is that of non-zero indexed arrays (which we don't have of course).

@fabiand
Copy link
Contributor

fabiand commented Mar 11, 2013

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?

@graydon
Copy link
Contributor

graydon commented Mar 11, 2013

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.

@jruderman
Copy link
Contributor

I don't like using foo.len() for the end because it's easy to mess up, and because foo might be a complicated expression I don't want to repeat.

@pcwalton
Copy link
Contributor

I don't think this is backwards incompatible.

@pcwalton
Copy link
Contributor

(Also I vote delaying this to Rust 2.0)

@pcwalton
Copy link
Contributor

Nominating for "not backwards incompatible".

@thestinger
Copy link
Contributor

The syntax should probably be &[start:end] or &mut [start:end] like indexing if we do add this.

@graydon
Copy link
Contributor

graydon commented Jul 18, 2013

yeah I think this is a feature or a far-future, not b-c.

@graydon
Copy link
Contributor

graydon commented Aug 1, 2013

accepted for far future milestone

@brson
Copy link
Contributor

brson commented Aug 1, 2013

This is a good and relatively uncontroversial project for a new compiler hacker.

@glaebhoerl
Copy link
Contributor

Wouldn't the : syntax conflict with @pcwalton's plan to have : be used for type ascription? I.e. in &[start:end] how would you know the meaning isn't "start is of type end"?

@erickt
Copy link
Contributor Author

erickt commented Aug 3, 2013

@glehel: I think it might. That's why I still like the v[1, .. 5] format. It keeps things consistent with our vec patterns.

@gsemet
Copy link

gsemet commented Nov 20, 2013

I fully endorse this syntaxic enhancement. I'm quite disappointed it would be delayed to "a far futur" however...

@nikomatsakis
Copy link
Contributor

Just for the record, I favor this strongly, and this is my preferred form.

  1. Add a Slice trait (see below).
  2. 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> { ... }

@pzol
Copy link
Contributor

pzol commented Jan 26, 2014

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.

@flaper87
Copy link
Contributor

cc me

I like this proposal!

@mneumann
Copy link
Contributor

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 than expr[..-2] and much less prone to errors, except that I would prefer expr.without_last or expr.excluding_last to be easier to understand, especially when it comes to initn which 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_lastn etc.

@glaebhoerl
Copy link
Contributor

Extending indexing from elements arr[i] to slices arr[i..j] is straightforward and easy to grok. I agree about providing dedicated methods instead for the remaining use cases.

@bstrie
Copy link
Contributor

bstrie commented Feb 13, 2014

+1 to nmatsakis' proposal verbatim. No comma, no syntax for length, no negative indices. Clean and simple.

@jakerr
Copy link
Contributor

jakerr commented Feb 13, 2014

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.

@Valloric
Copy link
Contributor

Another +1 for Niko's proposal. It's well thought-out.

@huonw
Copy link
Member

huonw commented Feb 14, 2014

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.

@molysgaard
Copy link

One more +1 for Niko's proposal.

@emberian
Copy link
Member

I don't think the complexity is worth it really.

@liigo
Copy link
Contributor

liigo commented Mar 16, 2014

+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.

  1. Add a Slice trait (see below).

  2. 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
.

@nikomatsakis
Copy link
Contributor

Clearly this is RFC material. I'm going to close this issue and open one on the rfcs repo :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C-enhancement Category: An issue proposing an enhancement or a PR with one.
Projects
None yet
Development

No branches or pull requests