Summary

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:

  • Removing most of the traits in collections.

  • A general

    一般
    proposal for solving the "equiv" problem, as well as improving MaybeOwned.

  • Patterns for overloading on by-need values and predicates.

  • Initial,

    初期
    forwards-compatible steps toward Iterable.

  • 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

初期
pass of some of the proposals here.

Motivation

This RFC aims to improve the design

設計(する)
of the std::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

定義する
several traits:

  • 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

さまざまな
concrete
具体的な/具象的な
collections has diverged; there is not a globally coherent design,
設計(する)
and there are many inconsistencies.

One problem in particular is the lack of clear guiding principles for the API design.

設計(する)
This RFC proposes a few along the way.

Providing
与える
slice APIs on Vec and String

The String and Vec types each provide a limited subset

部分集合
of the methods provides
与える
on string and vector slices, but there is not a clear reason to limit the API in this way. Today, one has to write things like some_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:

#![allow(unused)] fn main() { fn find(&self, k: &K) -> Option<&V> }

The problem is that, since K will be String, the find function requests a &String value -- whereas one typically

一般的に、典型的に
wants to work with the more flexible &str slices. In particular, using find with a literal string requires something like:

#![allow(unused)] fn main() { map.find(&"some literal".to_string()) }

which is unergonomic and requires an extra allocation

割当
just to get a borrow that, in some sense, was already available.

The current HashMap API works around this problem by providing

与える
an additional
追加の
set
セットする、集合
of methods that uses a generic notion of "equivalence" of values that have different types:

#![allow(unused)] fn main() { pub trait Equiv<T> { fn equiv(&self, other: &T) -> bool; } impl Equiv<str> for String { fn equiv(&self, other: &str) -> bool { self.as_slice() == other } } fn find_equiv<Q: Hash<S> + Equiv<K>>(&self, k: &Q) -> Option<&V> }

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

    シグネチャ
    of find_equiv is more difficult to understand than find, 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 for Ord.

The TreeMap API currently deals with this problem in an entirely different way:

#![allow(unused)] fn main() { /// Returns the value for which f(key) returns Equal. /// f is invoked with current key and guides tree navigation. /// That means f should be aware of natural ordering of the tree. fn find_with(&self, f: |&K| -> Ordering) -> Option<&V> }

Besides being less convenient -- you cannot write map.find_with("some literal") -- this function navigates the tree according

according to 〜に応じて
to an ordering
順序
that may have no relationship to the actual
実際の
ordering
順序
of the tree.

MaybeOwned

Sometimes a function does not know in advance whether it will need or produce

産出する
an owned copy of some data, or whether a borrow suffices. A typical example is the from_utf8_lossy function:

#![allow(unused)] fn main() { fn from_utf8_lossy<'a>(v: &'a [u8]) -> MaybeOwned<'a> }

This function will return a string slice if the input was correctly utf8 encoded

符号化する
-- without any allocation.
割当
But if the input has invalid utf8 characters,
文字
the function allocates
確保する
a new String and inserts
挿入する
utf8 "replacement characters"
文字
instead. Hence, the return type is an enum:

#![allow(unused)] fn main() { pub enum MaybeOwned<'a> { Slice(&'a str), Owned(String), } }

This interface makes it possible to allocate only when necessary, but the MaybeOwned type (and connected machinery) are somewhat ad

たす
hoc -- and specialized to String/str. It would be somewhat more palatable if there were a single
単一の
"maybe owned" abstraction usable across a wide range
範囲
of types.

Iterable

A frequently-requested feature for the collections module is an Iterable trait for "values that can be iterated

繰り返す、反復する
over". There are two main motivations:

  • 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). An Iterable trait could allow
    許可する、可能にする
    that.

  • Ergonomics. You'd be able to write

    #![allow(unused)] fn main() { for v in some_vec { ... } }

    rather than

    #![allow(unused)] fn main() { for v in some_vec.iter() { ... } }

    and consume_iter(some_vec) rather than consume_iter(some_vec.iter()).

Detailed design
設計(する)

The collections today

The concrete

具体的な/具象的な
collections currently available in std 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

主要な、初等の、第一の
goal of this RFC is to establish clean and consistent APIs that apply
適用する
across each group of collections.

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 concrete

具体的な/具象的な
collections are named according
according to 〜に応じて
to their implementation
実装
strategy, not the abstract semantics they implement.
実装する
We may eventually want PriorityQueue to be a trait that's implemented
実装する
by multiple
複数の
concrete
具体的な/具象的な
collections.

The LruCache could be renamed for a similar

似ている、同様の
reason (it uses a HashMap in its implementation), However, the implementation
実装
is actually generic with respect to this underlying
裏に潜んだ、背後の
map, and so in the long run (with HKT and other language
言語
changes) LruCache should probably add a type parameter
仮引数
for the underlying
裏に潜んだ、背後の
map, defaulted to HashMap.

Design
設計(する)
principles

  • Centering on Iterators. The Iterator 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 name find! 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

たす
and deprecated over time, a trait is "forever": there are very few backwards-compatible modifications to traits. Thus,
それゆえに、従って、
for something as fundamental as collections, it is prudent to take
とる
our time to get the traits right.

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,

言語
which makes directly
直接
defining
定義する
iterator methods in a trait impossible:

#![allow(unused)] fn main() { trait Iter { type A; type I: Iterator<&'a A>; // what is the lifetime here? fn iter<'a>(&'a self) -> I; // and how to connect it to self? } }

The problem is that, when implementing this trait, the return type I of iter should depend on the lifetime of self. For example, the corresponding

照応する
method in Vec looks like the following:

#![allow(unused)] fn main() { impl<T> Vec<T> { fn iter(&'a self) -> Items<'a, T> { ... } } }

This means that, given

与えられた
a Vec<T>, there isn't a single
単一の
type Items<T> for iteration
反復、繰り返し
-- rather, there is a family of types, one for each input lifetime. In other words, the associated
関連付けられた
type I in the Iter needs to be "higher-kinded": not just a single
単一の
type, but rather a family:

#![allow(unused)] fn main() { trait Iter { type A; type I<'a>: Iterator<&'a A>; fn iter<'a>(&self) -> I<'a>; } }

In this case, I is parameterized

仮引数
by a lifetime, but in other cases (like map) an associated
関連付けられた
type needs to be parameterized
仮引数
by a type.

In general,

一般
such higher-kinded types (HKTs) are a much-requested feature for Rust. But the design
設計(する)
and implementation
実装
of higher-kinded types is, by itself, a significant investment.

HKT would also allow

許可する、可能にする
for parameterization over smart pointer types, which has many potential use cases in the context
文脈、背景
of collections.

Thus,

それゆえに、従って、
the goal in this RFC is to do the best we can without HKT for now, while allowing
許可する、可能にする
a graceful migration if or when HKT is added.
たす

Persistent/immutable collections

Another problem with the current collection traits is the split between immutable

不変の
and mutable versions. In the long run, we will probably want to provide persistent collections (which allow
許可する、可能にする
non-destructive "updates" that create new collections that share most of their data with the old ones).

However, persistent collection APIs have not been thoroughly explored in Rust; it would be hasty to standardize on a set

セットする、集合
of traits until we have more experience.

Downsides of removal

There are two main downsides to removing the traits without a replacement:

  1. It becomes impossible to write code using generics over a "kind" of collection (like Map).

  2. It becomes more difficult to ensure

    保証する
    that the collections share a common API.

For point (1), first, if the APIs are sufficiently

十分に
consistent it should be possible to transition code from e.g. a TreeMap to a HashMap by changing very few lines of code. Second, generic programming
プログラミング
is currently quite limited, given
与えられた
the inability to iterate. Finally, generic programming
プログラミング
over collections is a large design
設計(する)
space (with much precedent in C++, for example), and we should take
とる
our time and gain more experience with a variety of concrete
具体的な/具象的な
collections before settling on a design.
設計(する)

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

セットする、集合
of conventions and APIs for concrete
具体的な/具象的な
collections up front, and stabilize on those, which should make it easy to add traits later on.

Why not leave the traits as "experimental"?

An alternative

代わりのもの、選択肢
to removal would be to leave the traits intact, but marked as experimental, with the intent to radically change them later.

Such a strategy doesn't buy much relative to removal (given the arguments

引数
above), but risks the traits becoming "de facto" stable if people begin using them en masse.

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 when T is instantiated with String.

A similar

似ている、同様の
story plays out for &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:

#![allow(unused)] fn main() { /// A trait for borrowing. trait Borrow<Sized? B> { /// Immutably borrow from an owned value. fn borrow(&self) -> &B; /// Mutably borrow from an owned value. fn borrow_mut(&mut self) -> &mut B; } // The Sized bound means that this impl does not overlap with the impls below. impl<T: Sized> Borrow<T> for T { fn borrow(a: &T) -> &T { a } fn borrow_mut(a: &mut T) -> &mut T { a } } impl Borrow<str> for String { fn borrow(s: &String) -> &str { s.as_slice() } fn borrow_mut(s: &mut String) -> &mut str { s.as_mut_slice() } } impl<T> Borrow<[T]> for Vec<T> { fn borrow(s: &Vec<T>) -> &[T] { s.as_slice() } fn borrow_mut(s: &mut Vec<T>) -> &mut [T] { s.as_mut_slice() } } }

(Note: thanks to @epdtry for suggesting this variation! The original proposal is listed

リスト、列挙する
in the Alternatives
代わりのもの、選択肢
.)

A primary

主要な、初等の、第一の
goal of the design
設計(する)
is allowing
許可する、可能にする
a blanket impl for non-sliceable types (the first impl above). This blanket impl ensures that all new sized, cloneable types are automatically
自動的に
borrowable; new impls are required only for new unsized types, which are rare.
まれ
The Sized bound
制限する、結び付けられて
on the initial
初期
impl means that we can freely add impls for unsized types (like str and [T]) without running afoul of coherence.

Because of the blanket impl, the Borrow trait can largely be ignored

無視する
except when it is actually used -- which we describe
記述する
next.

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:

#![allow(unused)] fn main() { impl<K,V> HashMap<K,V> where K: Hash + Eq { fn find<Q>(&self, k: &Q) -> &V where K: Borrow<Q>, Q: Hash + Eq { ... } fn contains_key<Q>(&self, k: &Q) -> bool where K: Borrow<Q>, Q: Hash + Eq { ... } fn insert(&mut self, k: K, v: V) -> Option<V> { ... } ... } }

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 both HashMap and TreeMap, and should work for other kinds of data structures as well. It also helps generalize MaybeOwned, for similar
    似ている、同様の
    reasons (see below.)

    A very important consequence

    結果
    is that the map methods using Borrow can potentially be put into a common Map trait that's implemented
    実装する
    by HashMap, 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 like find, contains_key and insert "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 blanket Equiv implementation.)

On the other hand, this approach retains some of the downsides of _equiv:

  • The signature

    シグネチャ
    for methods like find and contains_key is more complex
    複素数、複文の
    than their current signatures.
    シグネチャ
    There are two counterpoints. First, over time the Borrow 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 using find on HashMap<String, T>, various
    さまざまな
    method arguments
    引数
    just work as expected.

  • The API does not guarantee

    保証する
    "coherence": the Hash and Eq (or Ord, for TreeMap) implementations
    実装
    for the owned and borrowed keys might differ, breaking key invariants of the data structure. This is already the case with _equiv.

The Alternatives

section
includes a variant of Borrow 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 general

一般
version of the MaybeOwned as a "clone-on-write" smart pointer:

#![allow(unused)] fn main() { /// A generalization of Clone. trait FromBorrow<Sized? B>: Borrow<B> { fn from_borrow(b: &B) -> Self; } /// A clone-on-write smart pointer pub enum Cow<'a, T, B> where T: FromBorrow<B> { Shared(&'a B), Owned(T) } impl<'a, T, B> Cow<'a, T, B> where T: FromBorrow<B> { pub fn new(shared: &'a B) -> Cow<'a, T, B> { Shared(shared) } pub fn new_owned(owned: T) -> Cow<'static, T, B> { Owned(owned) } pub fn is_owned(&self) -> bool { match *self { Owned(_) => true, Shared(_) => false } } pub fn to_owned_mut(&mut self) -> &mut T { match *self { Shared(shared) => { *self = Owned(FromBorrow::from_borrow(shared)); self.to_owned_mut() } Owned(ref mut owned) => owned } } pub fn into_owned(self) -> T { match self { Shared(shared) => FromBorrow::from_borrow(shared), Owned(owned) => owned } } } impl<'a, T, B> Deref<B> for Cow<'a, T, B> where T: FromBorrow<B> { fn deref(&self) -> &B { match *self { Shared(shared) => shared, Owned(ref owned) => owned.borrow() } } } impl<'a, T, B> DerefMut<B> for Cow<'a, T, B> where T: FromBorrow<B> { fn deref_mut(&mut self) -> &mut B { self.to_owned_mut().borrow_mut() } } }

The type Cow<'a, String, str> is roughly equivalent

等価
to today's MaybeOwned<'a> (and Cow<'a, Vec<T>, [T]> to MaybeOwnedVector<'a, T>).

By implementing Deref and DerefMut, the Cow type acts

振る舞う
as a smart pointer -- but in particular, the mut variant actually clones if the pointed-to value is not currently owned. Hence "clone on write".

One slight gotcha with the design

設計(する)
is that &mut str is not very useful, while &mut String is (since it allows
許可する、可能にする
extending
拡張する
the string, for example). On the other hand, Deref and DerefMut must deref to the same underlying
裏に潜んだ、背後の
type, and for Deref to not require cloning, it must yield
産出する、出力する
a &str value.

Thus,

それゆえに、従って、
the Cow pointer offers a separate to_owned_mut method that yields
産出する、出力する
a mutable reference
参照
to the owned version of the type.

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 introduce

導入する
Borrow 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 form

形式、形態、形作る
of an Iterable trait is desirable for both expressiveness and ergonomics. Unfortunately, a full treatment of Iterable requires HKT for similar
似ている、同様の
reasons to the collection traits. However, it's possible to get some of the way there in a forwards-compatible fashion.

In particular, the following two traits work fine (with associated

items):

#![allow(unused)] fn main() { trait Iterator { type A; fn next(&mut self) -> Option<A>; ... } trait IntoIterator { type A; type I: Iterator<A = A>; fn into_iter(self) -> I; } }

Because IntoIterator consumes self, lifetimes are not an issue.

It's tempting to also define

定義する
a trait like:

#![allow(unused)] fn main() { trait Iterable<'a> { type A; type I: Iterator<&'a A>; fn iter(&'a self) -> I; } }

(along the lines of those proposed by an earlier RFC).

The problem with Iterable as defined

定義する
above is that it's locked to a particular lifetime up front. But in many cases, the needed lifetime is not even nameable in advance:

#![allow(unused)] fn main() { fn iter_through_rc<I>(c: Rc<I>) where I: Iterable<?> { // the lifetime of the borrow is established here, // so cannot even be named in the function signature for x in c.iter() { // ... } } }

To make this kind of example work, you'd need to be able to say something like:

#![allow(unused)] fn main() { where <'a> I: Iterable<'a> }

that is, that I implements

実装する
Iterable for every lifetime 'a. While such a feature is feasible to add to where clauses,
句、節
the HKT solution is undoubtedly cleaner.

Fortunately, we can have our cake and eat it too. This RFC proposes the IntoIterator trait above, together with the following blanket impl:

#![allow(unused)] fn main() { impl<I: Iterator> IntoIterator for I { type A = I::A; type I = I; fn into_iter(self) -> I { self } } }

which means that taking

とる
IntoIterator is strictly more flexible than taking
とる
Iterator. Note that in other languages
言語
(like Java), iterators are not iterable because the latter implies an unlimited number of iterations.
反復、繰り返し
But because IntoIterator consumes self, it yields
産出する、出力する
only a single
単一の
iteration,
反復、繰り返し
so all is good.

For individual

個々の、それぞれの
collections, one can then implement
実装する
IntoIterator on both the collection and borrows of it:

#![allow(unused)] fn main() { impl<T> IntoIterator for Vec<T> { type A = T; type I = MoveItems<T>; fn into_iter(self) -> MoveItems<T> { ... } } impl<'a, T> IntoIterator for &'a Vec<T> { type A = &'a T; type I = Items<'a, T>; fn into_iter(self) -> Items<'a, T> { ... } } impl<'a, T> IntoIterator for &'a mut Vec<T> { type A = &'a mut T; type I = ItemsMut<'a, T>; fn into_iter(self) -> ItemsMut<'a, T> { ... } } }

If/when HKT is added

たす
later on, we can add an Iterable trait and a blanket impl like the following:

#![allow(unused)] fn main() { // the HKT version trait Iterable { type A; type I<'a>: Iterator<&'a A>; fn iter<'a>(&'a self) -> I<'a>; } impl<'a, C: Iterable> IntoIterator for &'a C { type A = &'a C::A; type I = C::I<'a>; fn into_iter(self) -> I { self.iter() } } }

This gives a clean migration path: once Vec implements

実装する
Iterable, it can drop the IntoIterator impls for borrowed vectors, since they will be covered by the blanket implementation.
実装
No code should break.

Likewise, if we add a feature like the "universal" where clause

句、節
mentioned above, it can be used to deal with embedded
組み込み
lifetimes as in the iter_through_rc example; and if the HKT version of Iterable is later added,
たす
thanks to the suggested blanket impl for IntoIterator that where clause
句、節
could be changed to use Iterable 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, given

与えられた
the IntoIterator impls for Vec above, we would be able to write:

#![allow(unused)] fn main() { let v: Vec<Foo> = ... for x in &v { ... } // iterate over &Foo for x in &mut v { ... } // iterate over &mut Foo for x in v { ... } // iterate over Foo }

Similarly,

同様に
methods that currently take
とる
slices or iterators can be changed to take
とる
IntoIterator instead, immediately
直後に、直接的に
becoming more general
一般
and more ergonomic.

In general,

一般
IntoIterator will allow
許可する、可能にする
us to move toward more Iterator-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 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 are some drawbacks. For example, should Vec::map produce
産出する
an iterator, or a new vector? It would be possible to do the latter generically, but only with HKT. (See this discussion.)

This RFC only proposes to add the following method via IntoIterator, as a convenience for a common pattern:

#![allow(unused)] fn main() { trait IterCloned { type A; type I: Iterator<A>; fn iter_cloned(self) -> I; } impl<'a, T, I: IntoIterator> IterCloned for I where I::A = &'a T { type A = T; type I = ClonedItems<I>; fn into_iter(self) -> I { ... } } }

(The iter_cloned method will help reduce the number of method variants in general

一般
for collections, as we will see below).

We will leave to later RFCs the incorporation of additional

追加の
methods. Notice, in particular, that such methods can wait until we introduce
導入する
an Iterable trait via HKT without breaking backwards compatibility.
互換性

Minimizing variants: ByNeed and Predicate traits

There are several kinds of methods that, in their most general

一般
form
形式、形態、形作る
take
とる
closures, but for which convenience variants taking
とる
simpler data are common:

  • Taking

    とる
    values by need. For example, consider
    考える、みなす
    the unwrap_or and unwrap_or_else methods in Option:

    #![allow(unused)] fn main() { fn unwrap_or(self, def: T) -> T fn unwrap_or_else(self, f: || -> T) -> T }

    The unwrap_or_else method is the most general:

    一般
    it invokes
    呼び出す
    the closure to compute a default value only when self is None. 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, so unwrap_or provides
    与える
    a convenience wrapper.

  • Taking

    とる
    predicates. For example, a method like contains
    含む
    often shows up (inconsistently!) in two variants:

    #![allow(unused)] fn main() { fn contains(&self, elem: &T) -> bool; // where T: PartialEq fn contains_fn(&self, pred: |&T| -> bool) -> bool; }

    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:

#![allow(unused)] fn main() { trait ByNeed<T> { fn compute(self) -> T; } impl<T> ByNeed<T> for T { fn compute(self) -> T { self } } // Due to multidispatch, this impl does NOT overlap with the above one impl<T> ByNeed<T> for || -> T { fn compute(self) -> T { self() } } impl<T> Option<T> { fn unwrap_or<U>(self, def: U) where U: ByNeed<T> { ... } ... } }
#![allow(unused)] fn main() { trait Predicate<T> { fn check(&self, &T) -> bool; } impl<T: Eq> Predicate<T> for &T { fn check(&self, t: &T) -> bool { *self == t } } impl<T> Predicate<T> for |&T| -> bool { fn check(&self, t: &T) -> bool { (*self)(t) } } impl<T> Vec<T> { fn contains<P>(&self, pred: P) where P: Predicate<T> { ... } ... } }

Since these two patterns are particularly common throughout std, this RFC proposes adding

たす
both of the above traits, and using them to cut down on the number of method variants.

In particular, some methods on string slices currently work with CharEq, which is similar

似ている、同様の
to Predicate<char>:

#![allow(unused)] fn main() { pub trait CharEq { fn matches(&mut self, char) -> bool; fn only_ascii(&self) -> bool; } }

The difference is the only_ascii method, which is used to optimize

最適化する
certain operations
演算、操作
when the predicate only holds
保有する、有効である
for characters
文字
in the ASCII range.
範囲

To keep these optimizations intact while connecting to Predicate, this RFC proposes the following restructuring of CharEq:

#![allow(unused)] fn main() { pub trait CharPredicate: Predicate<char> { fn only_ascii(&self) -> bool { false } } }

Why not leverage unboxed closures?

A natural question is: why not use the traits for unboxed closures to achieve a similar

似ている、同様の
effect? For example, you could imagine writing a blanket impl for Fn(&T) -> bool for any T: PartialEq, which would allow
許可する、可能にする
PartialEq values to be used anywhere
どこでも
a predicate-like closure was requested.

The problem is that these blanket impls will often conflict. In particular, any type T could implement

実装する
Fn() -> T, and that single
単一の
blanket impl would preclude any others (at least, assuming that unboxed closure traits treat
取り扱う
the argument
引数
and return types as associated
関連付けられた
(output) types).

In addition,

追加
the explicit
明示的な
use of traits like 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

さまざまな
concrete
具体的な/具象的な
collections. These APIs will often be given
与えられた
in tabular form,
形式、形態、形作る
grouping together common APIs across multiple
複数の
collections. When writing these function signatures:
シグネチャ

  • We will assume

    仮定する
    a type parameter
    仮引数
    T for Vec, BinaryHeap, DList and RingBuf; we will also use this parameter
    仮引数
    for APIs on String, where it should be understood as char.

  • We will assume

    仮定する
    type parameters
    仮引数
    K: Borrow and V for HashMap and TreeMap; for TrieMap and SmallIntMap the K is assumed to be uint

  • We will assume

    仮定する
    a type parameter
    仮引数
    K: Borrow for HashSet and TreeSet; for BitvSet it is assumed to be uint.

We will begin by outlining the most widespread APIs in tables, making it easy to compare names and signatures

シグネチャ
across different kinds of collections. Then we will focus on some APIs specific
特定の
to particular classes
分類
of collections -- e.g. sets
セットする、集合
and maps. Finally, we will briefly discuss APIs that are specific
特定の
to a single
単一の
concrete
具体的な/具象的な
collection.

Construction
構築

All of the collections should support a static

静的な
function:

#![allow(unused)] fn main() { fn new() -> Self }

that creates an empty

空の
version of the collection; the constructor
function Object() { [native code] }
may take
とる
arguments
引数
needed to set
セットする、集合
up the collection, e.g. the capacity
容量
for LruCache.

Several collections also support separate constructors

function Object() { [native code] }
for providing
与える
capacities
容量
in advance; these are discussed below.

The FromIterator trait

All of the collections should implement

実装する
the FromIterator trait:

#![allow(unused)] fn main() { pub trait FromIterator { type A: fn from_iter<T>(T) -> Self where T: IntoIterator<A = A>; } }

Note that this varies from today's FromIterator by consuming an IntoIterator rather than Iterator. As explained above, this choice is strictly more general

一般
and will not break any existing code.

This constructor

function Object() { [native code] }
initializes
初期化
the collection with the contents of the iterator. For maps, the iterator is over key/value pairs, and the semantics is equivalent
等価
to inserting
挿入する
those pairs in order;
順序
if keys are repeated, the last value is the one left in the map.

Insertion
挿入

The table below gives methods for inserting

挿入する
items into various
さまざまな
concrete
具体的な/具象的な
collections:

OperationCollections
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) -> boolHashSet, 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 and RingBuf data structures no longer provide push, but rather push_front and push_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 a swap method, which has been dropped (consolidating needless method variants.)

Aside from these changes, a number of insertion

挿入
methods will be deprecated (e.g. the append and append_one methods on Vec). These are discussed further
さらなる、それ以上
in the section
on "specialized operations"
演算、操作
below.

The Extend trait (was: Extendable)

In addition

追加
to the standard insertion
挿入
operations
演算、操作
above, all collections will implement
実装する
the Extend trait. This trait was previously called
呼び出し
Extendable, but in general
一般
we prefer to avoid
避ける、回避する
-able suffixes and instead name the trait using a verb (or, especially, the key method offered by the trait.)

The Extend trait allows

許可する、可能にする
data from an arbitrary
任意の
iterator to be inserted
挿入する
into a collection, and will be defined
定義する
as follows:
下記の、次に続く、追従する

#![allow(unused)] fn main() { pub trait Extend: FromIterator { fn extend<T>(&mut self, T) where T: IntoIterator<A = Self::A>; } }

As with FromIterator, this trait has been modified to take

とる
an IntoIterator value.

Deletion

The table below gives methods for removing items into various

さまざまな
concrete
具体的な/具象的な
collections:

OperationCollections
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) -> boolHashSet, 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

挿入
methods, there are some differences from today's API:

  • The DList and RingBuf data structures no longer provide pop, but rather pop_front and pop_back -- similarly

    同様に
    to the push methods.

  • The remove method on maps returns the value previously associated

    関連付けられた
    with the key, if any. Previously, this functionality was provided
    与える
    by a separate pop method, which has been dropped (consolidating needless method variants.)

  • The retain method takes

    とる
    a Predicate.

  • The truncate, retain and dedup methods are offered more widely.

Again, some of the more specialized methods are not discussed here; see "specialized operations"

演算、操作
below.

Inspection/mutation

The next table gives methods for inspection and mutation of existing items in collections:

OperationCollections
fn len(&self) -> uintall
fn is_empty(&self) -> boolall
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) -> boolHashSet, TreeSet, TrieSet, EnumSet
fn contains_key(&self, &K) -> boolHashMap, TreeMap, TrieMap, SmallIntMap

The biggest changes from the current APIs are:

  • The find and find_mut methods have been renamed to get and get_mut. Further,

    さらなる、それ以上
    all get methods return Option values and do not invoke fail!. This is part of a general
    一般
    convention described
    記述する
    in the next section
    (on the Index traits).

  • The contains

    含む
    method is offered more widely.

  • There is no longer an equivalent

    等価
    of find_copy (which should be called
    呼び出し
    find_clone). Instead, we propose to add the following method to the Option<&'a T> type where T: Clone:

    #![allow(unused)] fn main() { fn cloned(self) -> Option<T> { self.map(|x| x.clone()) } }

    so that some_map.find_copy(key) will instead be written some_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 of Option benefit from the new convenience method.

The Index trait

The Index and IndexMut traits provide indexing notation

記法
like v[0]:

#![allow(unused)] fn main() { pub trait Index { type Index; type Result; fn index(&'a self, index: &Index) -> &'a Result; } pub trait IndexMut { type Index; type Result; fn index_mut(&'a mut self, index: &Index) -> &'a mut Result; } }

These traits will be implemented

実装する
for: [T], Vec, RingBuf, HashMap, TreeMap, TrieMap, SmallIntMap.

As a general

一般
convention, implementation
実装
of the Index traits will fail the task if the index is invalid (out of bounds
制限する、結び付けられて
or key not found); they will therefor return direct references
参照
to values. Any collection implementing Index (resp. IndexMut) should also provide a get method (resp. get_mut) as a non-failing variant that returns an Option value.

This allows

許可する、可能にする
us to keep indexing notation
記法
maximally concise, while still providing
与える
convenient non-failing variants (which can be used to provide a check for index validity).

Iteration
反復、繰り返し

Every collection should provide the standard trio of iteration

反復、繰り返し
methods:

#![allow(unused)] fn main() { fn iter(&'a self) -> Items<'a>; fn iter_mut(&'a mut self) -> ItemsMut<'a>; fn into_iter(self) -> ItemsMove; }

and in particular implement

実装する
the IntoIterator trait on both the collection type and on (mutable) references
参照
to it.

Capacity
容量
management
管理

many of the collections have some notion of "capacity",

容量
which may be fixed, grow explicitly,
明示的に
or grow implicitly:
暗黙的に

  • 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

容量
management,
管理
as follows.
下記の、次に続く、追従する

Explicit
明示的な
growth

For explicitly-grown collections, the normal constructor

function Object() { [native code] }
(new) takes
とる
a capacity
容量
argument.
引数
Capacity
容量
can later be inspected or updated as follows:
下記の、次に続く、追従する

#![allow(unused)] fn main() { fn capacity(&self) -> uint fn set_capacity(&mut self, capacity: uint) }

(Note, this renames LruCache::change_capacity to set_capacity, the prevailing style for setter method.)

Implicit
暗黙の
growth

For implicitly-grown collections, the normal constructor

function Object() { [native code] }
(new) does not take
とる
a capacity,
容量
but there is an explicit
明示的な
with_capacity constructor,
function Object() { [native code] }
along with other functions to work with the capacity
容量
later on:

#![allow(unused)] fn main() { fn with_capacity(uint) -> Self fn capacity(&self) -> uint fn reserve(&mut self, additional: uint) fn reserve_exact(&mut self, additional: uint) fn shrink_to_fit(&mut self) }

There are some important changes from the current APIs:

  • The reserve and reserve_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. The reserve function may grow the capacity
    容量
    by a larger amount than requested, to ensure
    保証する
    amortization, while reserve_exact will reserve exactly
    正確に
    the requested additional
    追加の
    capacity.
    容量
    The reserve_additional methods are deprecated.

  • The with_capacity constructor

    function Object() { [native code] }
    does not take
    とる
    any additional
    追加の
    arguments,
    引数
    for uniformity with new. This change affects Bitv in particular.

Bounded
制限する、結び付けられて
iterators

Some of the maps (e.g. TreeMap) currently offer specialized iterators over their entries

項目
starting at a given
与えられた
key (called lower_bound) and above a given
与えられた
key (called upper_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):

#![allow(unused)] fn main() { Bound<T> { /// An inclusive bound Included(T), /// An exclusive bound Excluded(T), Unbounded, } /// Creates a double-ended iterator over a sub-range of the collection's items, /// starting at min, and ending at max. If min is `Unbounded`, then it will /// be treated as "negative infinity", and if max is `Unbounded`, then it will /// be treated as "positive infinity". Thus range(Unbounded, Unbounded) will yield /// the whole collection. fn range(&self, min: Bound<&T>, max: Bound<&T>) -> RangedItems<'a, T>; fn range_mut(&self, min: Bound<&T>, max: Bound<&T>) -> RangedItemsMut<'a, T>; }

These iterators should be provided

与える
for any maps over ordered keys (TreeMap, TrieMap and SmallIntMap).

In addition,

追加
analogous methods should be provided
与える
for sets
セットする、集合
over ordered keys (TreeSet, TrieSet, BitvSet).

Set
セットする、集合
operations
演算、操作

Comparisons

All sets

セットする、集合
should offer the following methods, as they do today:

#![allow(unused)] fn main() { fn is_disjoint(&self, other: &Self) -> bool; fn is_subset(&self, other: &Self) -> bool; fn is_superset(&self, other: &Self) -> bool; }

Combinations

Sets

セットする、集合
can also be combined
合体する、組み合わせる
using the standard operations
演算、操作
-- union, intersection, difference and symmetric difference (exclusive or). Today's APIs for doing so look like this:

#![allow(unused)] fn main() { fn union<'a>(&'a self, other: &'a Self) -> I; fn intersection<'a>(&'a self, other: &'a Self) -> I; fn difference<'a>(&'a self, other: &'a Self) -> I; fn symmetric_difference<'a>(&'a self, other: &'a Self) -> I; }

where the I type is an iterator over keys that varies by concrete

具体的な/具象的な
set.
セットする、集合
Working with these iterators avoids
避ける、回避する
materializing intermediate
中間の、中級の
sets
セットする、集合
when they're not needed; the collect method can be used to create sets
セットする、集合
when they are. This RFC proposes to keep these names intact, following the RFC on iterator conventions.

Sets

セットする、集合
should also implement
実装する
the BitOr, BitAnd, BitXor and Sub traits from std::ops, allowing
許可する、可能にする
overloaded notation
記法
|, &, |^ and - to be used with sets.
セットする、集合
These are equivalent
等価
to invoking
呼び出す
the corresponding
照応する
iter_ method and then calling
呼び出し
collect, but for some sets
セットする、集合
(notably BitvSet) a more efficient
効率のよい
direct implementation
実装
is possible.

Unfortunately, we do not yet have a set

セットする、集合
of traits corresponding
照応する
to operations
演算、操作
|=, &=, etc, but again in some cases doing the update in place may be more efficient.
効率のよい
Right now, BitvSet is the only concrete
具体的な/具象的な
set
セットする、集合
offering such operations:
演算、操作

#![allow(unused)] fn main() { fn union_with(&mut self, other: &BitvSet) fn intersect_with(&mut self, other: &BitvSet) fn difference_with(&mut self, other: &BitvSet) fn symmetric_difference_with(&mut self, other: &BitvSet) }

This RFC punts on the question of naming here: it does not propose a new set

セットする、集合
of names. Ideally, we would add operations
演算、操作
like |= in a separate RFC, and use those conventionally for sets.
セットする、集合
If not, we will choose fallback names during the stabilization of BitvSet.

Map operations
演算、操作

Combined
合体する、組み合わせる
methods

The HashMap type currently provides

与える
a somewhat bewildering set
セットする、集合
of find/insert variants:

#![allow(unused)] fn main() { fn find_or_insert(&mut self, k: K, v: V) -> &mut V fn find_or_insert_with<'a>(&'a mut self, k: K, f: |&K| -> V) -> &'a mut V fn insert_or_update_with<'a>(&'a mut self, k: K, v: V, f: |&K, &mut V|) -> &'a mut V fn find_with_or_insert_with<'a, A>(&'a mut self, k: K, a: A, found: |&K, &mut V, A|, not_found: |&K, A| -> V) -> &'a mut V }

These methods are used to couple together lookup and insertion/update operations,

演算、操作
thereby avoiding
避ける、回避する
an extra lookup step. However, the current set
セットする、集合
of method variants seems overly complex.
複素数、複文の

There is another RFC already in the queue

キュー
addressing this problem in a very nice way, and this RFC defers to that one

Key and value iterators

In addition

追加
to the standard iterators, maps should provide by-reference convenience iterators over keys and values:

#![allow(unused)] fn main() { fn keys(&'a self) -> Keys<'a, K> fn values(&'a self) -> Values<'a, V> }

While these iterators are easy to define

定義する
in terms
項、用語
of the main iter method, they are used often enough to warrant including convenience methods.

Specialized operations
演算、操作

Many concrete

具体的な/具象的な
collections offer specialized operations
演算、操作
beyond the ones given
与えられた
above. These will largely be addressed through the API stabilization process (which focuses on local API issues, as opposed
反対する、対置する
to general
一般
conventions), but a few broad points are addressed below.

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 implement

実装する
Deref<[T]> and DerefMut<[T]>, and String to implement
実装する
Deref<str>. This will automatically
自動的に
allow
許可する、可能にする
all slice methods to be invoked
呼び出す
from vectors and strings, and will allow
許可する、可能にする
writing &*v rather than v.as_slice().

In this scheme, Vec and String are really "smart pointers" around the corresponding

照応する
slice types. While counterintuitive at first, this perspective actually makes a fair amount of sense, especially with DST.

(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,

符号化する
and some operations
演算、操作
are only efficient
効率のよい
when working at the byte level (and thus
それゆえに、従って、
taking
とる
this encoding
符号化する
into account).

As a general

一般
principle, we will move the API toward the following convention: index-related operations
演算、操作
always work in terms
項、用語
of bytes, other operations
演算、操作
deal with chars by default (but can have suffixed variants for working at other granularities when appropriate.)

DList

The DList type offers a number of specialized methods:

#![allow(unused)] fn main() { swap_remove, insert_when, insert_ordered, merge, rotate_forward and rotate_backward }

Prior to stabilizing the DList API, we will attempt to simplify its API surface, possibly

ことによると、可能性としてあり得ることに
by using idea from the collection views RFC.

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:

#![allow(unused)] fn main() { // on Vec<T> fn partition(self, f: |&T| -> bool) -> (Vec<T>, Vec<T>); // on [T] where T: Clone fn partitioned(&self, f: |&T| -> bool) -> (Vec<T>, Vec<T>); }

These two functions transform a vector/slice into a pair of vectors, based

基となる、基底(の)
on a "partitioning" function that says which of the two vectors to place elements
要素
into. The partition variant works by moving elements
要素
of the vector, while partitioned 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

代わりのもの、選択肢
design:
設計(する)

#![allow(unused)] fn main() { pub enum Either<T, U> { pub Left(T), pub Right(U), } impl<A, B> FromIterator for (A, B) where A: Extend, B: Extend { fn from_iter<I>(mut iter: I) -> (A, B) where I: IntoIterator<Either<T, U>> { let mut left: A = FromIterator::from_iter(None::<T>); let mut right: B = FromIterator::from_iter(None::<U>); for item in iter { match item { Left(t) => left.extend(Some(t)), Right(u) => right.extend(Some(u)), } } (left, right) } } trait Iterator { ... fn partition(self, |&A| -> bool) -> Partitioned<A> { ... } } // where Partitioned<A>: Iterator<A = Either<A, A>> }

This design

設計(する)
drastically generalizes
一般
the partitioning functionality, allowing
許可する、可能にする
it be used with arbitrary
任意の
collections and iterators, while removing the by-reference and by-value distinction.

Using this design,

設計(する)
you have:

#![allow(unused)] fn main() { // The following two lines are equivalent: let (u, w) = v.partition(f); let (u, w): (Vec<T>, Vec<T>) = v.into_iter().partition(f).collect(); // The following two lines are equivalent: let (u, w) = v.as_slice().partitioned(f); let (u, w): (Vec<T>, Vec<T>) = v.iter_cloned().partition(f).collect(); }

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,

追加
partitioning is supported for any iterator.

Removing methods like from_elem, from_fn, grow, and grow_fn

Vectors and some other collections offer constructors

function Object() { [native code] }
and growth functions like the following:

#![allow(unused)] fn main() { fn from_elem(length: uint, value: T) -> Vec<T> fn from_fn(length: uint, op: |uint| -> T) -> Vec<T> fn grow(&mut self, n: uint, value: &T) fn grow_fn(&mut self, n: uint, f: |uint| -> T) }

These extra variants can easily be dropped in favor of iterators, and this RFC proposes to do so.

The iter module already contains

含む
a Repeat 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:

#![allow(unused)] fn main() { // Equivalent: let v = Vec::from_elem(n, a); let v = Vec::from_iter(repeat(a).take(n)); // Equivalent: let v = Vec::from_fn(n, f); let v = Vec::from_iter(range(0, n).map(f)); // Equivalent: v.grow(n, a); v.extend(repeat(a).take(n)); // Equivalent: v.grow_fn(n, f); v.extend(range(0, n).map(f)); }

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 ad

たす
hoc method variants like grow_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:

#![allow(unused)] fn main() { // The following are *semantically* equivalent v.push_all(some_slice); v.extend(some_slice.iter_cloned()); // The following are *semantically* equivalent v.push_all_move(some_vec); v.extend(some_vec); }

However, currently the push_all and push_all_move methods can rely on the exact size of the container being pushed, in order

順序
to elide
省略する
bounds
制限する、結び付けられて
checks. We do not currently have a way to "trust" methods like len on iterators to elide
省略する
bounds
制限する、結び付けられて
checks. A separate RFC will introduce
導入する
the notion of a "trusted" method which should support such optimization and allow
許可する、可能にする
us to deprecate the push_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:

#![allow(unused)] fn main() { /// A trait for borrowing. /// If `T: Borrow` then `&T` represents data borrowed from `T::Owned`. trait Borrow for Sized? { /// The type being borrowed from. type Owned; /// Immutably borrow from an owned value. fn borrow(&Owned) -> &Self; /// Mutably borrow from an owned value. fn borrow_mut(&mut Owned) -> &mut Self; } trait ToOwned: Borrow { /// Produce a new owned value, usually by cloning. fn to_owned(&self) -> Owned; } impl<A: Sized> Borrow for A { type Owned = A; fn borrow(a: &A) -> &A { a } fn borrow_mut(a: &mut A) -> &mut A { a } } impl<A: Clone> ToOwned for A { fn to_owned(&self) -> A { self.clone() } } impl Borrow for str { type Owned = String; fn borrow(s: &String) -> &str { s.as_slice() } fn borrow_mut(s: &mut String) -> &mut str { s.as_mut_slice() } } impl ToOwned for str { fn to_owned(&self) -> String { self.to_string() } } impl<T> Borrow for [T] { type Owned = Vec<T>; fn borrow(s: &Vec<T>) -> &[T] { s.as_slice() } fn borrow_mut(s: &mut Vec<T>) -> &mut [T] { s.as_mut_slice() } } impl<T> ToOwned for [T] { fn to_owned(&self) -> Vec<T> { self.to_vec() } } impl<K,V> HashMap<K,V> where K: Borrow + Hash + Eq { fn find(&self, k: &K) -> &V { ... } fn insert(&mut self, k: K::Owned, v: V) -> Option<V> { ... } ... } pub enum Cow<'a, T> where T: ToOwned { Shared(&'a T), Owned(T::Owned) } }

This approach ties Borrow directly

直接
to the borrowed data, and uses an associated
関連付けられた
type to uniquely determine
決定する
the corresponding
照応する
owned data type.

For string keys, we would use HashMap<str, V>. Then, the find method would take

とる
an &str key argument,
引数
while insert would take
とる
an owned String. 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
与えられた
in the alternatives
代わりのもの、選択肢
section
.

Benefits of this alternative

代わりのもの、選択肢
:

  • Unlike the current _equiv or find_with methods, or the proposal in the RFC, this approach guarantees

    保証する
    coherence about hashing or ordering.
    順序
    For example, HashMap above requires that K (the borrowed key type) is Hash, 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 current find, insert, etc.

  • Like the proposal in this RFC, there is only a single

    単一の
    Borrow trait, so it would be possible to standardize on a Map trait later on and include these APIs. The trait could be made somewhat simpler with this alternative
    代わりのもの、選択肢
    form
    形式、形態、形作る
    of Borrow, 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 than HashMap<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

    適用する
    to Cow.

  • 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

与えられた
in the associated
関連付けられた
items RFC
):

#![allow(unused)] fn main() { trait HashMapKey : Clone + Hash + Eq { type Query: Hash = Self; fn compare(&self, other: &Query) -> bool { self == other } fn query_to_key(q: &Query) -> Self { q.clone() }; } impl HashMapKey for String { type Query = str; fn compare(&self, other: &str) -> bool { self.as_slice() == other } fn query_to_key(q: &str) -> String { q.into_string() } } impl<K,V> HashMap<K,V> where K: HashMapKey { fn find(&self, q: &K::Query) -> &V { ... } } }

This solution has several drawbacks, however:

  • It requires a separate trait for different kinds of maps -- one for HashMap, one for TreeMap, 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

与える
some help for the _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:

    #![allow(unused)] fn main() { map.find("some static string".as_string()) // with the hack map.find("some static string") // with this RFC }
  • 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 elements

要素
from v, while for x in v.iter() yields
産出する、出力する
references,
参照
may be a bit surprising. On the other hand, moving is the default almost everywhere in Rust, and with the proposed approach you get to use & and &mut to easily select other forms
形式、形態、形作る
of iteration.
反復、繰り返し

(See @huon's comment for additional

追加の
drawbacks.)

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 it's not obvious how to do it.

Input versus output type parameters
仮引数

An important aspect of the IntoIterator design

設計(する)
is that the element
要素
type is an associated
関連付けられた
type, not an input type.

This is a tradeoff:

  • Making it an associated

    関連付けられた
    type means that the for examples work, because the type of Self 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 when A is cloned, therefore implicitly
    暗黙的に
    cloning as needed to make the ownership work out (and obviating the need for iter_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

設計(する)
tradeoffs were discussed inline.

Unresolved questions

Unresolved conventions/APIs

As mentioned above, this RFC does not resolve

解決する
the question of what to call
呼び出し
set
セットする、集合
operations
演算、操作
that update the set
セットする、集合
in place.

It likewise does not settle the APIs that appear

現れる
in only single
単一の
concrete
具体的な/具象的な
collections. These will largely be handled through the API stabilization process, unless radical changes are proposed.

Finally, additional

追加の
methods provided
与える
via the IntoIterator 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

一般
story about such coercions will be left to a follow-up RFC.