- Start Date: 2014-10-29
- RFC PR #: rust-lang/rfcs#235
- Rust Issue #: rust-lang/rust#18424
Summary
This is a combinedstd::collections
.
The major components
-
Removing most of the traits in
collections
. -
A general
一般proposal for solving the "equiv" problem, as well as improvingMaybeOwned
. -
Patterns for overloading on by-need values and predicates.
-
Initial,
初期forwards-compatible steps towardIterable
. -
A coherent set
セットする、集合of API conventions across the full variety of collections.
A big thank-you to @Gankro, who helped collect API information and worked through an initial
Motivation
This RFC aims to improve the designstd::collections
module in preparation for API stabilization. There are a number of problems that need to be addressed, as spelled out in the subsections below.
Collection traits
The collections
module defines
- Collection
- Mutable
- MutableSeq
- Deque
- Map, MutableMap
- Set,セットする、集合MutableSet
There are several problems with the current trait design:
-
Most important: the traits do not provide iterator methods like
iter
. It is not possible to do so in a clean way without higher-kinded types, as the RFC explains in more detail below. -
The split between mutable and immutable
不変のtraits is not well-motivated by any of the existing collections. -
The methods defined
定義するin these traits are somewhat anemic compared比較するto the suite of methods provided与えるon the concrete具体的な/具象的なcollections that implement実装するthem.
Divergent APIs
Despite the current collection traits, the APIs of various
One problem in particular is the lack of clear guiding principles for the API design.
Providing与える slice APIs on Vec
and String
The String
and Vec
types each provide a limited subsetsome_str.as_slice().contains(...)
, which is not ergonomic or intuitive.
The Equiv
problem
There is a more subtle problem related to slices. It's common to use a HashMap
with owned String
keys, but then the natural API for things like lookup is not very usable:
The problem is that, since K
will be String
, the find
function requests a &String
value -- whereas one typically&str
slices. In particular, using find
with a literal string requires something like:
which is unergonomic and requires an extra allocation
The current HashMap
API works around this problem by providing
There are a few downsides to this approach:
-
It requires a duplicated
_equiv
variant of each method takingとるa reference参照to the key. (This downside could likely be mitigated using multidispatch.) -
Its correctness depends on equivalent
等価values producing産出、産出するthe same hash, which is not checked. -
String
-keyed hash maps are very common, so newcomers are likely to run headlong into the problem. First,find
will fail to work in the expected way. But the signatureシグネチャoffind_equiv
is more difficult to understand thanfind
, and it it's not immediately直後に、直接的にobvious that it solves the problem. -
It is the right API for
HashMap
, but not helpful for e.g.TreeMap
, which would want an analog forOrd
.
The TreeMap
API currently deals with this problem in an entirely different way:
Besides being less convenient -- you cannot write map.find_with("some literal")
-- this function navigates the tree according
MaybeOwned
Sometimes a function does not know in advance whether it will need or producefrom_utf8_lossy
function:
This function will return a string slice if the input was correctly utf8 encodedString
and insertsenum
:
This interface makes it possible to allocate only when necessary, but the MaybeOwned
type (and connected machinery) are somewhat adString
/str
. It would be somewhat more palatable if there were a single
Iterable
A frequently-requested feature for the collections
module is an Iterable
trait for "values that can be iterated
-
Abstraction. Today, you can write a function that takes
とるa single単一のIterator
, but you cannot write a function that takesとるa container and then iterates繰り返す、反復するover it multiple複数のtimes (perhaps with differing mutability levels). AnIterable
trait could allow許可する、可能にするthat. -
Ergonomics. You'd be able to write
rather than
and
consume_iter(some_vec)
rather thanconsume_iter(some_vec.iter())
.
Detailed design設計(する)
The collections today
The concretestd
fall into roughly three categories:
-
Sequences
連なり、並び- Vec
- String
- Slices
- Bitv
- DList
- RingBuf
- PriorityQueue
-
Sets
セットする、集合- HashSet
- TreeSet
- TrieSet
- EnumSet
- BitvSet
-
Maps
- HashMap
- TreeMap
- TrieMap
- LruCache
- SmallIntMap
The primary
Before diving into the details, there is one high-level changes that should be made to these collections. The PriorityQueue
collection should be renamed to BinaryHeap
, following the convention that concretePriorityQueue
to be a trait that's implemented
The LruCache
could be renamed for a similarHashMap
in its implementation), However, the implementationLruCache
should probably add a type parameterHashMap
.
Design設計(する) principles
-
Centering on
Iterator
s. TheIterator
trait is a strength of Rust's collections library. Because so many APIs can produce産出するiterators, addingたすan API that consumes one is very powerful -- and conversely as well. Moreover,その上iterators are highly efficient,効率のよいsince you can chain several layers of modification without having to materialize intermediate中間の、中級のresults.結果、戻り値Thus,それゆえに、従って、whenever possible, collection APIs should strive to work with iterators.In particular, some existing convenience methods avoid
避ける、回避するiterators for either performance or ergonomic reasons. We should instead improve the ergonomics and performance of iterators, so that these extra convenience methods are not necessary and so that all collections can benefit. -
Minimizing method variants. One problem with some of the current collection APIs is the proliferation of method variants. For example,
HashMap
include seven methods that begin with the namefind
! While each method has a motivation, the API as a whole can be bewildering, especially to newcomers.When possible, we should leverage the trait system, or find other abstractions, to reduce the need for method variants while retaining their ergonomics and power.
累乗 -
Conservatism. It is easier to add APIs than to take
とるthem away. This RFC takesとるa fairly conservative stance on what should be included in the collections APIs. In general,一般APIs should be very clearly motivated by a wide variety of use cases, either for expressiveness, performance, or ergonomics.
Removing the traits
This RFC proposes a somewhat radical step for the collections traits: rather than reform them, we should eliminate them altogether -- for now.
Unlike inherent methods, which can easily be added
Lack of iterator methods
In particular, there is one way in which the current traits are clearly wrong: they do not provide standard methods like iter
, despite these being fundamental to working with collections in Rust. Sadly, this gap is due to inexpressiveness in the language,
The problem is that, when implementing this trait, the return type I
of iter
should depend on the lifetime of self. For example, the correspondingVec
looks like the following:
This means that, givenVec<T>
, there isn't a singleItems<T>
for iterationI
in the Iter
needs to be "higher-kinded": not just a single
In this case, I
is parameterizedmap
) an associated
In general,
HKT would also allow
Thus,
Persistent/immutable collections
Another problem with the current collection traits is the split between immutable
However, persistent collection APIs have not been thoroughly explored in Rust; it would be hasty to standardize on a set
Downsides of removal
There are two main downsides to removing the traits without a replacement:
-
It becomes impossible to write code using generics over a "kind" of collection (like
Map
). -
It becomes more difficult to ensure
保証するthat the collections share a common API.
For point (1), first, if the APIs are sufficientlyTreeMap
to a HashMap
by changing very few lines of code. Second, generic programming
For point (2), first, the current traits have failed to keep the APIs in line, as we will see below. Second, this RFC is the antidote: we establish a clear set
Why not leave the traits as "experimental"?
An alternative
Such a strategy doesn't buy much relative to removal (given the arguments
Solving the _equiv
and MaybeOwned
problems
The basic problem that leads to _equiv
methods is that:
&String
and&str
are not the same type.- The
&str
type is more flexible and hence more widely used. - Code written for a generic type
T
that takesとるa reference参照&T
will therefore not be suitable whenT
is instantiated withString
.
A similar&Vec<T>
and &[T]
, and with DST and custom slice types the same problem will arise elsewhere.
The Borrow
trait
This RFC proposes to use a trait, Borrow
to connect borrowed and owned data in a generic fashion:
(Note: thanks to @epdtry for suggesting this variation! The original proposal is listed
A primaryimpl
for non-sliceable types (the first impl
above). This blanket impl
ensures that all new sized, cloneable types are automaticallyimpl
s are required only for new unsized types, which are rare.Sized
boundstr
and [T]
) without running afoul of coherence.
Because of the blanket impl
, the Borrow
trait can largely be ignored
Using Borrow
to replace _equiv
methods
With the Borrow
trait in place, we can eliminate the _equiv
method variants by asking map keys to be Borrow
:
The benefits of this approach over _equiv
are:
-
The
Borrow
trait captures the borrowing relationship between an owned data structure and both references参照to it and slices from it -- once and for all. This means that it can be used anywhereどこでもwe need to program generically over "borrowed" data. In particular, the single単一のtrait works for bothHashMap
andTreeMap
, and should work for other kinds of data structures as well. It also helps generalizeMaybeOwned
, for similar似ている、同様のreasons (see below.)A very important consequence
結果is that the map methods usingBorrow
can potentially be put into a commonMap
trait that's implemented実装するbyHashMap
,TreeMap
, and others. While we do not propose to do so now, we definitely want to do so later on. -
When using a
HashMap<String, T>
, all of the basic methods likefind
,contains_key
andinsert
"just work", without forcing you to think about&String
vs&str
. -
We don't need separate
_equiv
variants of methods. (However, this could probably be addressed with multidispatch by providing与えるa blanketEquiv
implementation.)
On the other hand, this approach retains some of the downsides of _equiv
:
-
The signature
シグネチャfor methods likefind
andcontains_key
is more complex複素数、複文のthan their current signatures.シグネチャThere are two counterpoints. First, over time theBorrow
trait is likely to become a well-known concept, so the signatureシグネチャwill not appear現れるcompletely alien. Second, what is perhaps more important than the signatureシグネチャis that, when usingfind
onHashMap<String, T>
, variousさまざまなmethod arguments引数just work as expected. -
The API does not guarantee
保証する"coherence": theHash
andEq
(orOrd
, forTreeMap
) implementations実装for the owned and borrowed keys might differ, breaking key invariants of the data structure. This is already the case with_equiv
.
The AlternativesBorrow
that doesn't suffer from these downsides, but has some downsides of its own.
Clone-on-write (Cow
) pointers
A side-benefit of the Borrow
trait is that we can give a more generalMaybeOwned
as a "clone-on-write" smart pointer:
The type Cow<'a, String, str>
is roughly equivalentMaybeOwned<'a>
(and Cow<'a, Vec<T>, [T]>
to MaybeOwnedVector<'a, T>
).
By implementing Deref
and DerefMut
, the Cow
type actsmut
variant actually clones if the pointed-to value is not currently owned. Hence "clone on write".
One slight gotcha with the design&mut str
is not very useful, while &mut String
is (since it allowsDeref
and DerefMut
must deref to the same underlyingDeref
to not require cloning, it must yield&str
value.
Thus,Cow
pointer offers a separate to_owned_mut
method that yields
Note that, by not using into_owned
, the Cow
pointer itself may be owned by some other data structure (perhaps as part of a collection) and will internally track whether an owned copy is available.
Altogether, this RFC proposes to introduceBorrow
and Cow
as above, and to deprecate MaybeOwned
and MaybeOwnedVector
. The API changes for the collections are discussed below.
IntoIterator
(and Iterable
)
As discussed in earlier, some formIterable
trait is desirable for both expressiveness and ergonomics. Unfortunately, a full treatment of Iterable
requires HKT for similar
In particular, the following two traits work fine (with associated
Because IntoIterator
consumes self
, lifetimes are not an issue.
It's tempting to also define
(along the lines of those proposed by an earlier RFC).
The problem with Iterable
as defined
To make this kind of example work, you'd need to be able to say something like:
that is, that I
implementsIterable
for every lifetime 'a
. While such a feature is feasible to add to where
clauses,
Fortunately, we can have our cake and eat it too. This RFC proposes the IntoIterator
trait above, together with the following blanket impl
:
which means that takingIntoIterator
is strictly more flexible than takingIterator
. Note that in other languagesIntoIterator
consumes self
, it yields
For individualIntoIterator
on both the collection and borrows of it:
If/when HKT is addedIterable
trait and a blanket impl
like the following:
This gives a clean migration path: once Vec
implementsIterable
, it can drop the IntoIterator
impl
s for borrowed vectors, since they will be covered by the blanket implementation.
Likewise, if we add a feature like the "universal" where
clauseiter_through_rc
example; and if the HKT version of Iterable
is later added,impl
for IntoIterator
that where
clauseIterable
instead, again without breakage.
Benefits of IntoIterator
What do we gain by incorporating IntoIterator
today?
This RFC proposes that for
loops should use IntoIterator
rather than Iterator
. With the blanket impl
of IntoIterator
for any Iterator
, this is not a breaking change. However, givenIntoIterator
impl
s for Vec
above, we would be able to write:
Similarly,IntoIterator
instead, immediately
In general,IntoIterator
will allowIterator
-centric APIs today, in a way that's compatible with HKT tomorrow.
Additional追加の methods
Another typical desire for an Iterable
trait is to offer defaulted versions of methods that basically re-export iterator methods on containers (see the earlier RFC). Usually these methods would go through a referenceiter
method) rather than a moving iterator.
It is possible to add such methods using the designVec::map
produce
This RFC only proposes to add the following method via IntoIterator
, as a convenience for a common pattern:
(The iter_cloned
method will help reduce the number of method variants in general
We will leave to later RFCs the incorporation of additionalIterable
trait via HKT without breaking backwards compatibility.
Minimizing variants: ByNeed
and Predicate
traits
There are several kinds of methods that, in their most general
-
Taking
とるvalues by need. For example, consider考える、みなすtheunwrap_or
andunwrap_or_else
methods inOption
:The
unwrap_or_else
method is the most general:一般it invokes呼び出すthe closure to compute a default value only whenself
isNone
. When the default value is expensive to compute, this by-need approach helps. But often the default value is cheap, and closures are somewhat annoying to write, sounwrap_or
provides与えるa convenience wrapper. -
Taking
とるpredicates. For example, a method likecontains
often shows up (inconsistently!) in two variants:含むAgain, the
contains_fn
version is the more general,一般but it's convenient to provide a specialized variant when the element要素type can be compared比較するfor equality,同一性、等式to avoid避ける、回避するwriting explicit明示的なclosures.
As it turns out, with multidispatch) it is possible to use a trait to express these variants through overloading:
Since these two patterns are particularly common throughout std
, this RFC proposes adding
In particular, some methods on string slices currently work with CharEq
, which is similarPredicate<char>
:
The difference is the only_ascii
method, which is used to optimize
To keep these optimizations intact while connecting to Predicate
, this RFC proposes the following restructuring of CharEq
:
Why not leverage unboxed closures?
A natural question is: why not use the traits for unboxed closures to achieve a similarimpl
for Fn(&T) -> bool
for any T: PartialEq
, which would allowPartialEq
values to be used anywhere
The problem is that these blanket impl
s will often conflict. In particular, any type T
could implementFn() -> T
, and that singleimpl
would preclude any others (at least, assuming that unboxed closure traits treat
In addition,Predicate
makes the intended semantics more clear, and the overloading less surprising.
The APIs
Now we'll delve into the detailed APIs for the various
-
We will assume
仮定するa type parameter仮引数T
forVec
,BinaryHeap
,DList
andRingBuf
; we will also use this parameter仮引数for APIs onString
, where it should be understood aschar
. -
We will assume
仮定するtype parameters仮引数K: Borrow
andV
forHashMap
andTreeMap
; forTrieMap
andSmallIntMap
theK
is assumed to beuint
-
We will assume
仮定するa type parameter仮引数K: Borrow
forHashSet
andTreeSet
; forBitvSet
it is assumed to beuint
.
We will begin by outlining the most widespread APIs in tables, making it easy to compare names and signatures
Construction構築
All of the collections should support a static
that creates an emptyLruCache
.
Several collections also support separate constructors
The FromIterator
trait
All of the collections should implementFromIterator
trait:
Note that this varies from today's FromIterator
by consuming an IntoIterator
rather than Iterator
. As explained above, this choice is strictly more general
This constructor
Insertion挿入
The table below gives methods for inserting
Operation | Collections |
---|---|
fn push(&mut self, T) | Vec , BinaryHeap , String |
fn push_front(&mut self, T) | DList , RingBuf |
fn push_back(&mut self, T) | DList , RingBuf |
fn insert(&mut self, uint, T) | Vec , RingBuf , String |
fn insert(&mut self, K::Owned) -> bool | HashSet , TreeSet , TrieSet , BitvSet |
fn insert(&mut self, K::Owned, V) -> Option<V> | HashMap , TreeMap , TrieMap , SmallIntMap |
fn append(&mut self, Self) | DList |
fn prepend(&mut self, Self) | DList |
There are a few changes here from the current state of affairs:
-
The
DList
andRingBuf
data structures no longer providepush
, but ratherpush_front
andpush_back
. This change is based基となる、基底(の)on (1) viewing them as deques and (2) not giving priority to the "front" or the "back". -
The
insert
method on maps returns the value previously associated関連付けられたwith the key, if any. Previously, this functionality was provided与えるby aswap
method, which has been dropped (consolidating needless method variants.)
Aside from these changes, a number of insertionappend
and append_one
methods on Vec
). These are discussed further
The Extend
trait (was: Extendable
)
In additionExtend
trait. This trait was previously calledExtendable
, but in general-able
suffixes and instead name the trait using a verb (or, especially, the key method offered by the trait.)
The Extend
trait allows
As with FromIterator
, this trait has been modified to takeIntoIterator
value.
Deletion
The table below gives methods for removing items into various
Operation | Collections |
---|---|
fn clear(&mut self) | all |
fn pop(&mut self) -> Option<T> | Vec , BinaryHeap , String |
fn pop_front(&mut self) -> Option<T> | DList , RingBuf |
fn pop_back(&mut self) -> Option<T> | DList , RingBuf |
fn remove(&mut self, uint) -> Option<T> | Vec , RingBuf , String |
fn remove(&mut self, &K) -> bool | HashSet , TreeSet , TrieSet , BitvSet |
fn remove(&mut self, &K) -> Option<V> | HashMap , TreeMap , TrieMap , SmallIntMap |
fn truncate(&mut self, len: uint) | Vec , String , Bitv , DList , RingBuf |
fn retain<P>(&mut self, f: P) where P: Predicate<T> | Vec , DList , RingBuf |
fn dedup(&mut self) | Vec , DList , RingBuf where T: PartialEq |
As with the insertion
-
The
DList
andRingBuf
data structures no longer providepop
, but ratherpop_front
andpop_back
-- similarly同様にto thepush
methods. -
The
remove
method on maps returns the value previously associated関連付けられたwith the key, if any. Previously, this functionality was provided与えるby a separatepop
method, which has been dropped (consolidating needless method variants.) -
The
retain
method takesとるaPredicate
. -
The
truncate
,retain
anddedup
methods are offered more widely.
Again, some of the more specialized methods are not discussed here; see "specialized operations"
Inspection/mutation
The next table gives methods for inspection and mutation of existing items in collections:
Operation | Collections |
---|---|
fn len(&self) -> uint | all |
fn is_empty(&self) -> bool | all |
fn get(&self, uint) -> Option<&T> | [T] , Vec , RingBuf |
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 |
fn contains(&self, &K) -> bool | HashSet , TreeSet , TrieSet , EnumSet |
fn contains_key(&self, &K) -> bool | HashMap , TreeMap , TrieMap , SmallIntMap |
The biggest changes from the current APIs are:
-
The
find
andfind_mut
methods have been renamed toget
andget_mut
. Further,さらなる、それ以上allget
methods returnOption
values and do not invokefail!
. This is part of a general一般convention described記述するin the next section節(on theIndex
traits). -
The
contains
method is offered more widely.含む -
There is no longer an equivalent
等価offind_copy
(which should be called呼び出しfind_clone
). Instead, we propose to add the following method to theOption<&'a T>
type whereT: Clone
:so that
some_map.find_copy(key)
will instead be writtensome_map.find(key).cloned()
. This method chain is slightly longer, but is more clear and allows許可する、可能にするus to drop the_copy
variants. Moreover,その上all users ofOption
benefit from the new convenience method.
The Index
trait
The Index
and IndexMut
traits provide indexing notationv[0]
:
These traits will be implemented[T]
, Vec
, RingBuf
, HashMap
, TreeMap
, TrieMap
, SmallIntMap
.
As a generalIndex
traits will fail the task if the index is invalid (out of boundsIndex
(resp. IndexMut
) should also provide a get
method (resp. get_mut
) as a non-failing variant that returns an Option
value.
This allows
Iteration反復、繰り返し
Every collection should provide the standard trio of iteration
and in particular implementIntoIterator
trait on both the collection type and on (mutable) references
Capacity容量 management管理
many of the collections have some notion of "capacity",
- No capacity/fixed capacity:容量
DList
,TreeMap
,TreeSet
,TrieMap
,TrieSet
, slices,EnumSet
- Explicit明示的なgrowth:
LruCache
- Implicit暗黙のgrowth:
Vec
,RingBuf
,HashMap
,HashSet
,BitvSet
,BinaryHeap
Growable collections provide functions for capacity
Explicit明示的な growth
For explicitly-grown collections, the normal constructornew
) takes
(Note, this renames LruCache::change_capacity
to set_capacity
, the prevailing style for setter method.)
Implicit暗黙の growth
For implicitly-grown collections, the normal constructornew
) does not takewith_capacity
constructor,
There are some important changes from the current APIs:
-
The
reserve
andreserve_exact
methods now takeとるas an argument引数the extra space to reserve, rather than the final desired capacity,容量as this usage is vastly more common. Thereserve
function may grow the capacity容量by a larger amount than requested, to ensure保証するamortization, whilereserve_exact
will reserve exactly正確にthe requested additional追加のcapacity.容量Thereserve_additional
methods are deprecated. -
The
with_capacity
constructorfunction Object() { [native code] }does not takeとるany additional追加のarguments,引数for uniformity withnew
. This change affectsBitv
in particular.
Bounded制限する、結び付けられて iterators
Some of the maps (e.g. TreeMap
) currently offer specialized iterators over their entrieslower_bound
) and above a givenupper_bound
), along with _mut
variants. While the functionality is worthwhile, the names are not very clear, so this RFC proposes the following replacement API (thanks to @Gankro for the suggestion):
These iterators should be providedTreeMap
, TrieMap
and SmallIntMap
).
In addition,TreeSet
, TrieSet
, BitvSet
).
Setセットする、集合 operations演算、操作
Comparisons
All sets
Combinations
Sets
where the I
type is an iterator over keys that varies by concretecollect
method can be used to create sets
SetsBitOr
, BitAnd
, BitXor
and Sub
traits from std::ops
, allowing|
, &
, |^
and -
to be used with sets.iter_
method and then callingcollect
, but for some setsBitvSet
) a more efficient
Unfortunately, we do not yet have a set|=
, &=
, etc, but again in some cases doing the update in place may be more efficient.BitvSet
is the only concrete
This RFC punts on the question of naming here: it does not propose a new set|=
in a separate RFC, and use those conventionally for sets.BitvSet
.
Map operations演算、操作
Combined合体する、組み合わせる methods
The HashMap
type currently providesfind
/insert
variants:
These methods are used to couple together lookup and insertion/update operations,
There is another RFC already in the queue
Key and value iterators
In addition
While these iterators are easy to defineiter
method, they are used often enough to warrant including convenience methods.
Specialized operations演算、操作
Many concrete
Relating Vec
and String
to slices
One goal of this RFC is to supply all of the methods on (mutable) slices on Vec
and String
. There are a few ways to achieve this, so concretely the proposal is for Vec<T>
to implementDeref<[T]>
and DerefMut<[T]>
, and String
to implementDeref<str>
. This will automatically&*v
rather than v.as_slice()
.
In this scheme, Vec
and String
are really "smart pointers" around the corresponding
(Initially, it was unclear whether this strategy would play well with method resolution, but the planned resolution rules should work fine.)
String
API
One of the key difficulties with the String
API is that strings use utf8 encoding,
As a general
DList
The DList
type offers a number of specialized methods:
Prior to stabilizing the DList
API, we will attempt to simplify its API surface, possibly
Minimizing method variants via iterators
Partitioning via FromIterator
One place we can move toward iterators is functions like partition
and partitioned
on vectors and slices:
These two functions transform a vector/slice into a pair of vectors, basedpartition
variant works by moving elementspartitioned
clones elements.
There are a few unfortunate aspects of an API like this one:
-
It's specific
特定のto vectors/slices, although in principle both the source元のand target containers could be more general.一般 -
The fact that two variants have to be exposed, for owned versus clones, is somewhat unfortunate.
This RFC proposes the following alternative
This design
Using this design,
There is some extra verbosity, mainly due to the type annotations for collect
, but the API is much more flexible, since the partitioned data can now be collected into other collections (or even differing collections). In addition,
Removing methods like from_elem
, from_fn
, grow
, and grow_fn
Vectors and some other collections offer constructors
These extra variants can easily be dropped in favor of iterators, and this RFC proposes to do so.
The iter
module already containsRepeat
iterator; this RFC proposes to add a free function repeat
to iter
as a convenience for iter::Repeat::new
.
With that in place, we have:
While these replacements are slightly longer, an important aspect of ergonomics is memorability: by placing greater emphasis on iterators, programmers will quickly learn the iterator APIs and have those at their fingertips, while remembering adgrow_fn
is more difficult.
Long-term: removing push_all
and push_all_move
The push_all
and push_all_move
methods on vectors are yet more API variants that could, in principle, go through iterators:
However, currently the push_all
and push_all_move
methods can rely on the exact size of the container being pushed, in orderlen
on iterators to elidepush_all
and push_all_move
variants. (This is unlikely to happen before 1.0, so the methods will probably still be included with "experimental" status, and likely with different names.)
Alternatives代わりのもの、選択肢
Borrow
and the Equiv
problem
Variants of Borrow
The original version of Borrow
was somewhat more subtle:
This approach ties Borrow
directly
For string keys, we would use HashMap<str, V>
. Then, the find
method would take&str
key argument,insert
would takeString
. On the other hand, for some other type Foo
a HashMap<Foo, V>
would take&Foo
for find
and Foo
for insert
. (More discussion on the choice of ownership is given
Benefits of this alternative
-
Unlike the current
_equiv
orfind_with
methods, or the proposal in the RFC, this approach guarantees保証するcoherence about hashing or ordering.順序For example,HashMap
above requires thatK
(the borrowed key type) isHash
, and will produce産出するhashes from owned keys by first borrowing from them. -
Unlike the proposal in this RFC, the signature
シグネチャof the methods for maps is very simple -- essentially the same as the currentfind
,insert
, etc. -
Like the proposal in this RFC, there is only a single
単一のBorrow
trait, so it would be possible to standardize on aMap
trait later on and include these APIs. The trait could be made somewhat simpler with this alternative代わりのもの、選択肢form形式、形態、形作るofBorrow
, but can be provided与えるin either case; see these comments for details. -
The
Cow
data type is simpler than in the RFC's proposal, since it does not need a type parameter仮引数for the owned data.
Drawbacks of this alternative
-
It's quite subtle that you want to use
HashMap<str, T>
rather thanHashMap<String, T>
. That is, if you try to use a map in the "obvious way" you will not be able to use string slices for lookup, which is part of what this RFC is trying to achieve. The same applies適用するtoCow
. -
The design
設計(する)is somewhat less flexible than the one in the RFC, because (1) there is a fixed choice of owned type corresponding照応するto each borrowed type and (2) you cannot use multiple複数のborrow types for lookups at different types (e.g. using&String
sometimes and&str
other times). On the other hand, these restrictions制限guarantee保証するcoherence of hashing/equality/comparison. -
This version of
Borrow
, mapping from borrowed to owned data, is somewhat less intuitive.
On the balance, the approach proposed in the RFC seems better, because using the map APIs in the obvious ways works by default.
The HashMapKey
trait and friends
An earlier proposal for solving the _equiv
problem was given
This solution has several drawbacks, however:
-
It requires a separate trait for different kinds of maps -- one for
HashMap
, one forTreeMap
, etc. -
It requires that a trait be implemented
実装するon a given与えられたkey without providing与えるa blanket implementation.実装Since you also need different traits for different maps, it's easy to imagine cases where a out-of-crate type you want to use as a key doesn't implement実装するthe key trait, forcing you to newtype. -
It doesn't help with the
MaybeOwned
problem.
Daniel Micay's hack
@strcat has a PR that makes it possible to, for example, coerce a &str
to an &String
value.
This provides_equiv
problem, since the _equiv
methods could potentially be dropped. However, there are a few downsides:
-
Using a map with string keys is still a bit more verbose:
-
The solution is specialized to strings and vectors, and does not necessarily support user-defined unsized types or slices.
-
It doesn't help with the
MaybeOwned
problem. -
It exposes some representation
表現interplay between slices and references参照to owned values, which we may not want to commit to or reveal.
For IntoIterator
Handling of for
loops
The fact that for x in v
moves elementsv
, while for x in v.iter()
yields&
and &mut
to easily select other forms
(See @huon's comment for additional
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 callIntoIterator
gives you an iterator that can be used only once, while Iterable
allows
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 it's not obvious how to do it.
Input versus output type parameters仮引数
An important aspect of the IntoIterator
design
This is a tradeoff:
-
Making it an associated
関連付けられたtype means that thefor
examples work, because the type ofSelf
uniquely determines決定するthe element要素type for iteration,反復、繰り返しaiding type inference. -
Making it an input type would forgo those benefits, but would allow
許可する、可能にするsome additional追加のflexibility. For example, you could implement実装するIntoIterator<A>
for an iterator on&A
whenA
is cloned, therefore implicitly暗黙的にcloning as needed to make the ownership work out (and obviating the need foriter_cloned
). However, we have generally kept away from this kind of implicit暗黙のmagic, especially when it can involve hidden costs like cloning, so the more explicit明示的なdesign設計(する)given与えられたin this RFC seems best.
Downsides
Design
Unresolved questions
Unresolved conventions/APIs
As mentioned above, this RFC does not resolve
It likewise does not settle the APIs that appear
Finally, additionalIntoIterator
API are left for future consideration.
Coercions
Using the Borrow
trait, it might be possible to safely add a coercion for auto-slicing:
If T: Borrow:
coerce &'a T::Owned to &'a T
coerce &'a mut T::Owned to &'a mut T
For sized types, this coercion is forced to be trivial, so the only time it would involve running user code is for unsized values.
A general