Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.
Sign upRFC: Collections reform #235
Conversation
aturon
added some commits
Sep 5, 2014
This comment has been minimized.
This comment has been minimized.
|
cc #17 |
Gankro
reviewed
Sep 11, 2014
| impl<T: Eq> Predicate<T> for &T { | ||
| fn check(&self, t: &T) -> bool { | ||
| self == t |
This comment has been minimized.
This comment has been minimized.
Gankro
Sep 11, 2014
Contributor
Pretty sure these need to be dereffed, unless the semantics of operators have changed from underneath me.
Gankro
reviewed
Sep 11, 2014
| fn with_capacity(uint) -> Self | ||
| fn capacity(&self) -> uint | ||
| fn reserve(&mut self, uint) | ||
| fn reserve_exact(&mut self, uint) |
This comment has been minimized.
This comment has been minimized.
andrew-d
reviewed
Sep 11, 2014
| impl<T> Borrow for [T] { | ||
| type Owned = Vec<T>; | ||
| fn borrow(s: &Vec<T>) -> &[T] { |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Gankro
Sep 11, 2014
Contributor
It's a static function, not an instance method.
Edit: This is because many structures can implement Borrow for the same type. E.G. &String and &str are both Borrow for String as far as I can tell.
This comment has been minimized.
This comment has been minimized.
andrew-d
Sep 11, 2014
Ah, fair enough then. That being said: the selfs below should probably be changed to s then
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
P1start
Oct 5, 2014
Contributor
Now that borrow is an instance method, s should probably be changed to self.
Gankro
reviewed
Sep 11, 2014
| fn iter_from(&self, k: &K) -> Entries<'a, K, V> | ||
| fn iter_from_mut(&mut self, k: &K) -> EntriesMut<'a, K, V> | ||
| Returns an iterator starting with the first key-value pair whose key is greater than k. |
This comment has been minimized.
This comment has been minimized.
Gankro
reviewed
Sep 11, 2014
| ```rust | ||
| fn keys(&'a self) -> Keys<'a, K> | ||
| fn values(&'a self) -> Values<'a, V> |
This comment has been minimized.
This comment has been minimized.
alexcrichton
reviewed
Sep 11, 2014
| for x in &v { ... } // iterate over &Foo | ||
| for x in &mut v { ... } // iterate over &mut Foo | ||
| for x in v { ... } // iterate over Foo | ||
| ``` |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Sep 11, 2014
Member
This would also yield patterns like:
for x in v { ... } // iterate over Foo
for x in v.iter() { ... } // iterate over &FooThese both seem quite ergonomic and quite tempting, and look fairly similar, yet yield different results. I'd be ok with it, does it sound ok to you?
This comment has been minimized.
This comment has been minimized.
Gankro
Sep 11, 2014
Contributor
It's probably unlikely that you wouldn't notice very quickly if you used the wrong one. Although I could see this burning newbies.
This comment has been minimized.
This comment has been minimized.
aturon
Sep 11, 2014
Author
Member
@alexcrichton Yes, the fact that the "default" interpretation is a moving iterator is perhaps a bit weird. On the other hand, moving is the default almost everywhere in Rust, and with this approach you get to use & and &mut to easily select other forms of iteration.
Unfortunately, it's a bit tricky to make for use by-ref iterators instead. The problem is that an iterator is IntoIterator, but it is not Iterable (or whatever we call the by-reference trait). Why? Because IntoIterator gives you an iterator that can be used only once, while Iterable allows you to ask for iterators repeatedly.
If for demanded an Iterable, then for x in v.iter() and for x in v.iter_mut() would cease to work -- we'd have to find some other approach. It might be doable, but offhand I don't see how.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
zkamsler
Sep 12, 2014
Making for use IntoIterator exclusively would prevent one from using an Iterator after partially iterating over it with a for. This is possible now, since it is mutably borrowed. Ideally, for would treat an Iterator differently from a non-iterator, but then it would have to be more than a parser desugaring.
This comment has been minimized.
This comment has been minimized.
thestinger
Sep 16, 2014
I don't think moving out of a container should be the default iterator. I'm strongly against adding an Iterable trait until there's a proposal without it. Containers shouldn't be treated as cheap temporaries. Moving from a container via an iterator is far from being the common case. It's such an edge case that more sugar for it is totally unnecessary and will lead people down the wrong path.
This comment has been minimized.
This comment has been minimized.
pcwalton
Sep 16, 2014
Contributor
I don't see how moving out encourages people to treat containers to be cheap temporaries. You still have to create the container in the first place, via .collect() or Vec::new() or whatnot.
This comment has been minimized.
This comment has been minimized.
huonw
Sep 17, 2014
Member
It renders the container unusable later, so people will apply the clone hammer; e.g. with a scenario like
let v: Vec<int> = ...;
let mut result = 0;
for x in v {
result += f(x);
}
return (result, v);I'm 100% sure that there will be people who fix the compiler error with for x in v.clone() (and I would guess that it won't be a trivial number). I also have a feeling that consuming iteration is likely to be the least efficient in general:
- large values will be (in general) unoptimisably memcpy'd around
- when iterating over something complicated like treemap, consuming it will probably have to be quite careful to avoid crashes; possibly at the expense of speed
This comment has been minimized.
This comment has been minimized.
aturon
Sep 17, 2014
Author
Member
A couple of points here:
-
I don't see any simple way to make
foruse anIterable-style API that defaults to by-reference, for reasons that I outlined here. Basically, an iterator can only implement a by-valueIterableAPI.Please let me know if you see a way around this, though.
-
In terms of shaping what people do, note that the ergonomics of
for x in vandfor x in &vare almost identical. So I think the main thing is documentation. When we first introduce iteration in the docs, we would surely showfor x in &vand explain what the&is doing. If we make sure that programmers are made aware of that from their first encounter with iteration, I think it's unlikely that they'd reach for.clone. -
Moving is the default almost everywhere in Rust. To me, writing
for x in &vserves as a nice signal to the reader that references are being taken. It also plays quite nicely with the deref coercions that I've recently proposed.
This comment has been minimized.
This comment has been minimized.
huonw
Sep 17, 2014
Member
I do agree that it's hard to do a by ref iterator with a naive Iterable API.
There are some points in the other direction:
-
I believe unconditionally calling
IntoIterator(i.e. not dectecting and handlingIterators specially) will stop rust-lang/rust#8372 being possible, since the iteratorfor ... in iter { ... }wlll be moved (or duplicated) and thus eitheriterwill not be usable in the body, or it will not refer to the same thing as the for loop head. -
Some containers can only provide
&, i.e.&is the lowest common denominator. -
On that note, it is weird that
for x in vis by value forVecbut by reference for&[], assuming this is the case, anyway (and there's a similar difference forfor x in mape.g. ifmapis a function argument that is passed as&HashMap<...>vs. if it is a local). -
If we make sure that programmers are made aware of that from their first encounter with iteration, I think it's unlikely that they'd reach for .clone.
Well, people do a lot of passing
VecandStringby value now, even when they could just pass&if not&stror&[]; maybe that's just a problem with our documentation.
alexcrichton
reviewed
Sep 11, 2014
| fn cloned(self) -> Option<T> { | ||
| self.map(|x| x.clone()) | ||
| } | ||
| ``` |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Sep 11, 2014
Member
Could this actually take T: Borrow to convert Option<&str> to Option<String>?
This comment has been minimized.
This comment has been minimized.
aturon
Sep 11, 2014
Author
Member
It could indeed! (Well, it'd take T: ToOwned).
And perhaps, more generally, we may find that a ToOwned bound is more useful than a Clone bound in many cases, for exactly this reason.
alexcrichton
reviewed
Sep 11, 2014
| `fn get_mut(&mut self, uint) -> Option<&mut T>` | `[T]`, `Vec`, `RingBuf` | ||
| `fn get(&self, &K) -> Option<&V>` | `HashMap`, `TreeMap`, `TrieMap`, `SmallIntMap` | ||
| `fn get_mut(&mut self, &K) -> Option<&mut V>` | `HashMap`, `TreeMap`, `TrieMap`, `SmallIntMap` | ||
| `fn contains<P>(&self, P) where P: Predicate<T>` | `[T]`, `str`, `Vec`, `String`, `DList`, `RingBuf`, `BinaryHeap` |
This comment has been minimized.
This comment has been minimized.
alexcrichton
Sep 11, 2014
Member
For strings, if T is char, would it be possible to leverage this method to test if a string contains a substring?
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
aturon
Sep 11, 2014
Author
Member
Alternatively, we could use a double-dispatch trick to have the String contains method be more general than the interface given here. That's probably fine, and we can make the final decision as part of String stabilization.
This comment has been minimized.
This comment has been minimized.
|
I skimmed/skipped the first 1000 lines assuming it hasn't changed much since last I saw it. Overall I'm a big
|
This comment has been minimized.
This comment has been minimized.
|
@Gankro I addressed the various typos you pointed out.
I don't think methods like That said, we could consider using
That's certainly doable. It's a tradeoff, since it makes the API somewhat more complex for a pretty rare use case. My plan for key recovery was to go through your collection view's RFC, which offers a bit more of a swiss army knife for working with maps. We'll have to think about sets, though. |
aturon
added some commits
Sep 11, 2014
aturon
referenced this pull request
Sep 11, 2014
Closed
RFC for adding an `Iterable` trait family #17
alexcrichton
force-pushed the
rust-lang:master
branch
from
6357402
to
e0acdf4
Sep 11, 2014
nrc
assigned
aturon
Sep 11, 2014
This comment has been minimized.
This comment has been minimized.
epdtry
commented
Sep 11, 2014
|
It seems to me that it would be more flexible to define
(I'm not 100% clear about the rules for blanket Now if you define a custom string type, you can still get the benefits of
It also would let you have
Then
And you can still define
Did you consider implementing |
This comment has been minimized.
This comment has been minimized.
zkamsler
commented
Sep 12, 2014
|
I want to like the idea of |
This comment has been minimized.
This comment has been minimized.
|
Hmm, this just occurred to me. The proposal states we would "deprecate" the traits. Is this intended literally (as in #[deprecated]), or will they just be removed overnight? Due to naming conflicts I'm pretty sure that the traits can't coexist in a deprecated form with the proposed concrete impls. Or will we have a brief period where the traits are deprecated as a warning, with no (merged) replacement? |
This comment has been minimized.
This comment has been minimized.
|
This comment has been minimized.
This comment has been minimized.
|
@sfackler I assumed that wording was simply referring to the standard deprecation -> removal path. I was (am?) uncertain about the nature of the transition. |
This comment has been minimized.
This comment has been minimized.
reem
commented
Sep 13, 2014
|
I might just be being dense, but is there a reason for the name |
This comment has been minimized.
This comment has been minimized.
|
Copy on write I'd assume. |
This comment has been minimized.
This comment has been minimized.
|
One thing I keep remembering and forgetting, and don't really know where to put it, so I'm just going to put here since it's relevant to this work: Instead of all this
This iterator should be DoubleEnded and therefore reversible. This of course doesn't address the inclusive/exclusive scenario, for which we can either do the same thing as
which is the solution I prefer. |
alexcrichton
referenced this pull request
Sep 16, 2014
Closed
Feature request: Drop the iter() from vec. #17301
thestinger
reviewed
Sep 16, 2014
| methods would go through a reference iterator (i.e. the `iter` method) rather | ||
| than a moving iterator. | ||
|
|
||
| It is possible to add such methods using the design proposed above, but there |
This comment has been minimized.
This comment has been minimized.
thestinger
Sep 16, 2014
It shouldn't produce a vector. Creating containers is very expensive and lazy iterators should be encouraged. An API producing temporary container will result in countless accidentally temporaries, as demonstrated by the code conventions in the compiler.
thestinger
reviewed
Sep 16, 2014
| with these iterators avoids materializing intermediate sets when they're not | ||
| needed; the `collect` method can be used to create sets when they are. | ||
|
|
||
| To clarify the API, this RFC proposes renaming the methods to `iter_or`, |
This comment has been minimized.
This comment has been minimized.
thestinger
Sep 16, 2014
I don't think this would do a good job conveying that these are the basic set operations to most people. Unless looking up union in the documentation will find iter_or... I don't think it will today.
This comment has been minimized.
This comment has been minimized.
aturon
Sep 17, 2014
Author
Member
That's a fair point. But one problem is that there are really several versions of the "basic set operations": those returning iterators, those returning new sets, and those mutating self. I'd love to hear any proposal for a naming convention that covers all of these cases more clearly.
aturon commentedSep 11, 2014
This is a combined conventions and library stabilization RFC. The goal is to establish a set of naming and signature conventions for
std::collections.The major components of the RFC include:
collections.MaybeOwned.Iterable.A big thank-you to @Gankro, who helped collect API information and worked through an initial pass of some of the proposals here.
Rendered