- Feature Name:
linked_list_cursors
- Start Date: 2018-10-14
- RFC PR: rust-lang/rfcs#2570
- Rust Issue: rust-lang/rust#58533
Summary
Many of the benefits of linked listsCursor
interface can be created to efficiently edit linked lists.IterMut
changes will be removed.
Motivation
From Programming Rust:
As of Rust 1.12, Rust’s LinkedList type has no methods for removing a range
範囲of elements要素from a listリスト、列挙するor inserting挿入するelements要素at specific特定のlocations in a list.リスト、列挙するThe API seems incomplete.
Both of these issues have been fixed, but in different and incompatible ways. Removing a rangedrain_filter
API, and insertinglinked_list_extras
extensions to IterMut
.
This motivates the need for a standard interface for insertionDoubleEndedIterator
. However, mutable cursors can also edit the collection at their position.
A mutable cursor would allowIterMut
API and a complete
Guide-level explanation
The cursor interface would providesCursor
and CursorMut
. These are created in the same way as iterators.
With a Cursor
one can seek back and forth through a listCursorMut
One can seek back and forth and get mutable references
Lets look at where these might be useful.
Examples
This interface is helpful most times insertion
For example, consider
This could also be done using iterators. One could transform the list
For another example, considerIterMut
extensions.
fn main() {
let mut list: LinkedList<_> = (0..10).collect();
let mut iter = list.iter_mut();
while let Some(x) = iter.next() {
if x >= 5 {
break;
}
}
iter.insert_next(12);
}
This can be changed almost verbatim to CursorMut
:
fn main() {
let mut list: LinkedList<_> = (0..10).collect();
let mut cursor = list.cursor_front_mut() {
while let Some(x) = cursor.peek_next() {
if x >= 5 {
break;
}
cursor.move_next();
}
cursor.insert_after(12);
}
In general,
Reference-level explanation
One gets a cursor the exact same way as one would get an iterator. The returned cursor would point to the "empty"current
you would receive None
.
These would provide the following
One should closely considerCursor
and CursorMut
operate on data in their LinkedList
. This is why, they both hold'list
.
The lifetime elision for their constructors
becomes
which is what we would expect. (the same goes for CursorMut
).
Since Cursor
cannot mutate its list,current
, peek_next
and peek_prev
all live as long as 'list
. However, in CursorMut
we must be careful to make these methods borrow. Otherwise,
The only other lifetime annotation is with as_cursor
. In this case, the returned Cursor
must borrow its generatingCursorMut
. Otherwise,
One question that arises from this interface is what happens if move_next
is calledmove_prev
and the beginning). A simple way to solve this is to make cursors wrap around this listbool
, however this is unnecessary since current
is sufficient to know whether the iterator is at the end of the list.
A large consequenceIter
and IterMut
API. Therefore, the followingIterMut
in the linked_list_extras
features should be removed or depreciated:
IterMut::insert_next
IterMut::peek_next
The rest of the iterator methods are stable and should probably stay untouched (but see below for comments).
Drawbacks
The cursor interface is rather clunky, and while it allows
One of the largest issues with the cursor interface is that it exposes the exact same interface of iterators (and more), which leads to unnecessary code duplication. However, the purpose of iterators seems to be simple, abstract and easy to use rather than efficient
Rationale and alternatives代わりのもの、選択肢
There are several alternatives
- Implement実装するcursors as a trait extending
Iterator
(see the cursors pseudo-rfc below)
Since the cursors are just an extension of iterators, it makes some sense to create them as a trait. However, I see several reasons why this is not the best.
First, cursors work differently than the existing Iterator
extensions like DoubleEndedIterator
. In a DoubleEndedIterator
, if one callsnext_back
and then next
, it should not return the same value, so unlike a cursor, a DoubleEndedIterator
does not move back and forth throughout a collection.
Furthermore, while Iterator
is a generalCursor
is very much specificVec
a cursor does not make sense. So it makes little sense to make a trait when it will only be used in one place.
- Using the
IterMut
linked listリスト、列挙するextensions
InsertionIterMut
in the linked_list_extras
feature. Many of these features could be addedIterMut
with many methods that have nothing to do with iterationCursorMut
.
- Do not create cursors at all
Everything that cursors do can already be done, albeit in sometimes a less efficient
Prior art
This rust internals post describes
- Java-style iterators
Java (and other languages) tried to fix this by addingremove
function to their iterators. However, I feel this method would not be the best choice for Rust (even for specificIterMut
s like those in LinkedList) since it diverges from the expected behaviour of iterators.
Discussion on the issue tracker about how this is currently managed with modifications to IterMut
. The consensus seems to be that it is incomplete, and it is suggested to create a new Cursor
and CursorMut
types.
Unresolved questions
- How will this interface interact with iterators?
Will we keep both Iter
and Cursor
types? Implement
- Only for linked lists?
Should we implement