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

Remove the [] notation for taking a whole slice. #491

Closed
wants to merge 1 commit into from

Conversation

nrc
Copy link
Member

@nrc nrc commented Nov 29, 2014

@sinistersnare
Copy link

+1, &* actually makes more sense when you understand the system. If this doesn't go, I would prefer [..] too, as it uses the slice notation, for getting a slice.

@ftxqxd
Copy link
Contributor

ftxqxd commented Nov 29, 2014

I agree with this RFC—for slicing, ‘there should be one obvious way to do it’. I’ve switched from writing foo[] to writing &*foo partially because I hope the [] notation would go away and partially because it means I don’t have to go to the effort of enabling the slicing feature gate if I haven’t already. :P

Another advantage of this RFC is that we can remove slicing on strings. Being able to quickly convert a String to a &str is nice, so we implemented Slice on strings for the foo[] notation. However, implementing Slice also lets one use the foo[a..b] syntax, which is not a good thing—this syntax takes byte indices, not character or grapheme indices as one might expect or actually want. Indexing on strings was removed for this reason, so I’d love to see slicing removed, too.

@reem
Copy link

reem commented Nov 30, 2014

I've been doing much the same thing as @P1start, I find &* makes a lot of sense when you understand the system even though it's very confusing when you are just starting. Deref coercions will let you write &, which is much less confusing to the newcomer.

@nrc nrc self-assigned this Nov 30, 2014
@blaenk
Copy link
Contributor

blaenk commented Nov 30, 2014

Yesss please. The [] syntax has always bothered me, and I was disappointed that it was accepted to begin with, to the point that I explicitly write out .as_slice() instead. Also agree Deref coercions should be implemented :)

As I've said before, I also agree that in the worst case, [..] would indeed be preferable to [].

So I completely agree with this. Good one @nick29581!

@Valloric
Copy link

Valloric commented Dec 1, 2014

I find &* makes a lot of sense when you understand the system even though it's very confusing when you are just starting.

At the 1.0 gate, the number of people who are "just starting" will greatly outnumber the people who understand the system. foo[] shouldn't go away until &foo lands; &*foo is IMO a readability nightmare for newcomers. We'll have the same "Rust has too many sigil" comments we got when there were several pointer types.

The barrier to entry must be low.

@liigo
Copy link
Contributor

liigo commented Dec 1, 2014

foo[] shouldn't go away until &foo lands; &*foo is IMO a readability
nightmare for newcomers. We'll have the same "Rust has too many sigil"
comments we got when there were several pointer types.

+1, please hold it off currently

@reem
Copy link

reem commented Dec 1, 2014

I guess the best thing to do would be to get deref coercions pre-1.0, but since it's backwards compatible, that seems unlikely to happen.

@mcpherrinm
Copy link

If we allow [A..B], [A..], [B..], then I think we should support [..]: All four are the same operation, just with defaults for A & B. (0 and .len()).

Really I just want to support everything python slice notation does. (even if the : has fallen over sideways), which would additionally include support for negative indexing

@Ericson2314
Copy link
Contributor

Big +1, for the reasons already stated.

@glaebhoerl
Copy link
Contributor

I agree with @mcpherrinm: we should keep it as [..] for logical consistency if nothing else.

(even if the : has fallen over sideways)

This is cute; I hadn't thought of it this way. :)

@mitsuhiko
Copy link
Contributor

&* is a pretty terrible pattern to be honest and absolutely confusing for users. I consider [] much clearer because it says "get this as a slice". Especially x[mut] is a lot more readable than &mut *x.

@thestinger
Copy link

&* is a pretty terrible pattern to be honest and absolutely confusing for users. I consider [] much clearer because it says "get this as a slice". Especially x[mut] is a lot more readable than &mut *x.

It's not confusing to anyone with even a basic grasp of the language, and I think it's perfectly readable. I don't think delaying the need to learn the basics by hiding it behind magical sugar is going to help newcomers. There is a reason that people find very basic languages without lots of magic like Go to be easy to pick up and use.

@thestinger
Copy link

@nick29581: I strongly support your proposal. I think removing duplicate features / unnecessary magic is a very important part of making a usable language. Using consistent and explicit syntax is often better than having specialized sugar for many cases. This syntax is already widely used and relies on the fundamental core of the language so I don't buy the claims that is confusing at all.

@thestinger
Copy link

At the 1.0 gate, the number of people who are "just starting" will greatly outnumber the people who understand the system. foo[] shouldn't go away until &foo lands; &*foo is IMO a readability nightmare for newcomers. We'll have the same "Rust has too many sigil" comments we got when there were several pointer types.

The barrier to entry must be low.

Sugar increases the barrier to entry. It makes the language harder to grasp by adding special cases. Every Rust user needs to understand the basics of borrowing and dereferencing, and adding special sugar for this case just adds one more thing they need to keep in their head. Arguing for additional complexity / syntax is not helping newcomers.

@thestinger
Copy link

AFAIK, the proposal add a magical borrowing operator was rejected. It would make &(x) and &x into different operations which is hardly going to be helpful to newcomers. It just adds more special cases / magic that everyone needs to learn to grasp the core language.

@arthurprs
Copy link

[ ] is consistent(ish) with slicing syntax and it's less noisy, I vote to keep it.
"deref then borrow" sucks badly, most people just recognize the pattern. The brain reads "full slice" much better.

@thestinger
Copy link

@mcpherrinm: I wouldn't mind having [..] simply for consistency in another language, but Rust already has a simple way to do this built into the core language. In Python, the [:] operation on the built-in list type performs a shallow copy - it's not a no-op, and it doesn't provide a view. Rust doesn't have that use case, and already has operator sugar to get a slice from a vector - which is consistent with the syntax to get an &T reference from another type like Rc<T> or Box<T>. It is not obscure, but rather a core feature of the language that's used everywhere.

@thestinger
Copy link

This is a coherent, simple system:

  • Box<T> -> &T is done with &*x
  • Rc<T> -> &T is done with &*x
  • Arc<T> -> &T is done with &*x
  • Vec<T> -> &[T] is done with &*x
  • String -> &str is done with &*x

Having an additional way to do it for slices is not making the language simpler or easier to learn. Users will encounter the pattern in many other places, and they will also encounter code doing it the other way for slices because it's so obvious. I don't buy that a larger grammar makes the code easier to learn. Both xs[] and &*xs are the same number of characters, and the latter has the benefit of being familiar due to usage everywhere else.

@cl91
Copy link

cl91 commented Dec 2, 2014

@mitsuhiko @arthurprs The problem is that if you have a vector of strings, you will have to write v[1][] to get a slice, which makes v look like a multidimensional array. In this case &_v[1] will look better (or even better &v[1]). There was a discussion on reddit about how to get the cmd arguments where they have to do os.args[1][], which is arguably less good looking than &os.args[1] or &_os.args[1].

@thestinger
Copy link

I think it's important to keep in mind that the xs[] syntax would always live alongside the &*xs syntax. Neither will be more idiomatic / common because there are clearly a lot of people who prefer &*xs. It just fragments the community into different dialects for no good reason, which is C++ disease (but they have the excuse of backwards compatibility, we don't).

@arthurprs
Copy link

@thestinger I understand your reasoning but we should still have a syntax for full slice (even if deref-and-borrow results in the same thing).

@thestinger
Copy link

Why should we have another way of doing the same thing? I'm not hearing any reasons that don't amount to the claim that extra magic / complexity / inconsistent syntax makes life easier for newcomers.

@thestinger
Copy link

Note that adding this syntax could happen at any point in the future, but deciding between [] and [..] or removing it completely cannot ever be taken back. The safe decision is removing it as it's clearly a duplicate feature, and xs[..] is also a noisier way of writing &*xs. It can be added at any point in the future if for some reason it turns out to be wanted, but I don't see why that would happen...

@thestinger
Copy link

Oh, and not only does it imply duplicate syntax but also a duplicate method. It's very weird to have two separate traits / methods for these types to implement that are identical and overload a different thing...

@blaenk
Copy link
Contributor

blaenk commented Dec 2, 2014

Yeah, to repeat, I'm strongly in favor of removing it, and in the absolute
worst case changing it to [..] to at least achieve consistency.

I'm not sure on the status of &String coercions but we should also
prioritize that, though I agree that &* on its own is still better than the
current syntax.

@arthurprs
Copy link

I can't think of any examples that would make sense to have different implementations for borrow and full-slice but that doesn't mean they don't exist.

@thestinger
Copy link

The ability for someone to provide two different implementations despite all the standard cases being the same makes it that much more confusing.

@Earnestly
Copy link

All this sugar does is make it harder for newcomers to really understand the language, at least from my experience as a newcomer to many languages.

Sugar will always eventually become a pitfall when the same logic suddenly doesn't apply in a certain context. I personally this very frustrating as it now requires constantly having to remember the differences and how they do or don't work in some places.

If you're not bound by having to support legacy systems, please consider very carefully when adding sugar in general!

@mcpherrinm
Copy link

My argument about consistency doesn't really hold with the way the std::ops::Slice trait is written today: [..] would be an additional method on the trait (as [] currently is). Instead, we could always call slice_or_fail with Option<&Idx> parameters: [..N] would be None, Some(&N) and [..] would be None, None.

In that case [..] is not special at all. This proposal is potentially worthy of an RFC if it's a supportable idea. I haven't examined any of the implementations of slicing today, so I have no idea if this is an impedance match with how the implementation works.

++ to removing [] and special handling of that case. If we support [..], it should not be because we explicitly handle it, but because it falls out of a design to do so.

@SiegeLord
Copy link

While &* and [] are analogous for built-in collections, they are not always in 3rd party code. For example, if I wanted to write a library that does do a clone using the [] operator (or if not a clone, some lazy slicing object), I would not be able to do so. The current equivalence of &* and [] is solely due to misdesign of the Slice trait (as it does not allow returning something by value). If the Slice traits were redesigned, this equivalence would go away.

@thestinger
Copy link

@SiegeLord: The ability to overload by-value dereference would cover the same use case, so I don't see why that matters. I think it was a bit questionable to implement dereferencing for Vec<T> / String as there were other ways to do code reuse and it's a bit weird. However, that's now the convention set by the standard library. It doesn't make sense to add magical sugar with the sole purpose of having the option to deviate from the established conventions.

@bstrie
Copy link
Contributor

bstrie commented Dec 3, 2014

+1 to removing []. This and if-let are the two places where Rust starts slipping into TIMTOWTDI (though the difference is that I could probably live with a limited version of if-let).

@lilyball
Copy link
Contributor

lilyball commented Dec 3, 2014

+1 to removing [] if #241 lands. Without it, &*x is really kind of a gross introduction for newcomers. Sure, it makes sense once you understand the full system and how it works, but it's going to be rather off-putting to anyone who doesn't have deep knowledge of Rust.

I am slightly concerned that removing [] will be detrimental to types that implement Slice but not Deref, but I can't think of any good reasons to avoid implementing Deref on a type that implements Slice.

@thestinger
Copy link

I'm strongly against #241. Having &x where x is Vec<T> return &[T] just puts up yet another complex barrier to learning the language. I don't understand how a proposal to have &(x) and &x be different operations can be taken seriously.

@thestinger
Copy link

Attempting to paper over the inherent complexity in the memory model with magical sugar does not make the language simpler. It makes the language more complex by adding lots of special cases with non-obvious workarounds. Rust has already tried going down that path, and it never turns out well. Why are special cases like that even still on the table?

@Gankra
Copy link
Contributor

Gankra commented Dec 3, 2014

@thestinger With respect, few people are as familiar with the language's storied history as you are. Further, it is not uncommon to find decisions in the compiler or standard libraries that made sense at the time, but are now dubious in the context of current conventions and design. Rust is very-much-so a moving target. With that in mind, it doesn't seem unreasonable to revisit previously discussed design issues every now and then. Or at very least, it would be more helpful to direct interested parties to the previous discussions so that they can benefit from everyone's past experiences. This would likely be more productive than simply dismissing such discussion as "already tried".

(I do not have a particularly strong opinion on the specific issue at hand, I just think that revisiting/scrutinizing past decisions is important)

@ftxqxd
Copy link
Contributor

ftxqxd commented Dec 4, 2014

@thestinger #241 doesn’t propose making &x and &(x) different. That’s #248, which was closed.

@arthurprs
Copy link

@SiegeLord wrote it better than I could. I'm all in favor of zen of python but in my opinion we should preserve [ ] as it may not be equivalent to borrow in all cases.

@aturon
Copy link
Member

aturon commented Dec 4, 2014

@thestinger

I'm strongly against #241. Having &x where x is Vec<T> return &[T] just puts up yet another complex barrier to learning the language. I don't understand how a proposal to have &(x) and &x be different operations can be taken seriously.

Just a heads-up: that's not what RFC 241 proposes. The RFC you're talking about was closed a while ago.

@liigo
Copy link
Contributor

liigo commented Dec 4, 2014

I don't think we should replace an ugly [] with another ugly &*, which
don't have an obvious meaning of "as slice". -1 from me.

@nrc
Copy link
Member Author

nrc commented Dec 4, 2014

@liigo we're not talking about replacing - &* works already (in theory, if not practice). We're only talking about removing [].

@pythonesque
Copy link
Contributor

+1 to this RFC, kill [] with fire. When you want as_slice(), write as_slice().

@nrc
Copy link
Member Author

nrc commented Dec 6, 2014

We (the Rust team) discussed this in some depth today. The arguments for and against were pretty much as already outlined in the RFC.

We are pretty much agreed that we will not see any form of cross-borrowing coercion in Rust for 1.0, therefore, with this RFC, we would be living with the &*Foo syntax for taking a slice. We agreed that this was "really bad". There was some concern too about using deref where it did not make sense - multi-dimensional matrices were one example.

Importantly, we clarified that under RFC #439, the syntax for creating a slice is &foo[] (as opposed to the current syntax foo[]), and so when/if we implement cross-borrowing coercions, the options for taking a whole slice would be &foo[] or &foo (assuming foo is a Vec or something similar). Although this means there are still two ways of getting a whole slice for many collections, the difference between the two is not as gross as I had assumed (it is simply omitting [] rather than changing from using the [] suffix to the & prefix).

On the issue of [] vs [..], we decided to keep the current syntax ([]) because of potential ambiguities and because it does not make sense to have an open range .. outside of an index/slice operation. Also because, with RFC #498 we don't have ..n ranges either, we feel that [..] is not significantly more consistent with other ranges than []. (And of course [] is more convenient for the common operation of taking a whole slice).

@nrc nrc closed this Dec 6, 2014
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.