Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix linked list cursor names #2847

Merged
merged 4 commits into from
Jan 14, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 103 additions & 94 deletions text/2570-linked-list-cursors.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,21 @@ list many times. With the cursor interface, one can do the following:

``` rust
fn remove_replace<T, P, F>(list: &mut LinkedList<T>, p: P, f: F)
where P: Fn(&T) -> bool, F: Fn(T) -> T
where P: Fn(&T) -> bool, F: Fn(T) -> T
{
let mut cursor = list.cursor_mut();
// move to the first element, if it exists
loop {
let should_replace = match cursor.peek() {
Some(element) => p(element),
None => break,
};
if should_replace {
let old_element = cursor.pop().unwrap();
cursor.insert(f(old_element));
}
cursor.move_next();
}
let mut cursor = list.cursor_front_mut();
// move to the first element, if it exists
loop {
let should_replace = match cursor.peek_next() {
Some(element) => p(element),
None => break,
};
if should_replace {
let old_element = cursor.remove_current().unwrap();
cursor.insert_after(f(old_element));
}
cursor.move_next();
}
}
```

Expand All @@ -84,31 +84,31 @@ iterator, perform operations on it and collect. This is easier, however it still
requires much needless allocation.

For another example, consider code that was previously using `IterMut`
extensions.
extensions.
``` rust
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);
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`:
``` rust
fn main() {
let mut list: LinkedList<_> = (0..10).collect();
let mut cursor = list.cursor_mut() {
while let Some(x) = cursor.peek_next() {
if x >= 5 {
break;
}
cursor.move_next();
}
cursor.insert(12);
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, the cursor interface is not the easiest way to do something.
Expand All @@ -122,90 +122,99 @@ One gets a cursor the exact same way as one would get an iterator. The
returned cursor would point to the "empty" element, i.e. if you got an element
and called `current` you would receive `None`.
``` rust
// Provides a cursor to the first element of the list
pub fn cursor(&self) -> Cursor<T>;
/// Provides a cursor to the first element of the list.
pub fn cursor_front(&self) -> Cursor<T>;

/// Provides a cursor with mutable references and access to the list
pub fn cursor_mut(&mut self) -> CursorMut<T>;
/// Provides a mutable cursor to the first element of the list.
pub fn cursor_front_mut(&mut self) -> CursorMut<T>;

/// Provides a cursor to the last element of the list.
pub fn cursor_back(&self) -> Cursor<T>;

/// Provides a mutable cursor to the last element of the list.
pub fn cursor_back_mut(&mut self) -> CursorMut<T>;
```

These would provide the following interface:

``` rust
impl<'list, T> Cursor<'list, T> {
/// Move to the subsequent element of the list if it exists or the empty
/// element
pub fn move_next(&mut self);
/// Move to the previous element of the list
pub fn move_prev(&mut self);

/// Get the current element
pub fn current(&self) -> Option<&'list T>;
/// Get the following element
pub fn peek(&self) -> Option<&'list T>;
/// Get the previous element
pub fn peek_before(&self) -> Option<&'list T>;
/// Returns the cursor position index within the `LinkedList`.
pub fn index(&self) -> Option<usize>;

/// Move to the subsequent element of the list if it exists or the empty
/// element
pub fn move_next(&mut self);
/// Move to the previous element of the list
pub fn move_prev(&mut self);

/// Get the current element
pub fn current(&self) -> Option<&'list T>;
/// Get the following element
pub fn peek_next(&self) -> Option<&'list T>;
/// Get the previous element
pub fn peek_prev(&self) -> Option<&'list T>;
}

impl<'list T> CursorMut<'list, T> {
/// Move to the subsequent element of the list if it exists or the empty
/// element
pub fn move_next(&mut self);
/// Move to the previous element of the list
pub fn move_prev(&mut self);

/// Get the current element
pub fn current(&mut self) -> Option<&mut T>;
/// Get the next element
pub fn peek(&mut self) -> Option<&mut T>;
/// Get the previous element
pub fn peek_before(&mut self) -> Option<&mut T>;

/// Get an immutable cursor at the current element
pub fn as_cursor<'cm>(&'cm self) -> Cursor<'cm, T>;

// Now the list editing operations

/// Insert `item` after the cursor
pub fn insert(&mut self, item: T);
/// Insert `item` before the cursor
pub fn insert_before(&mut self, item: T);

/// Remove and return the item following the cursor
pub fn pop(&mut self) -> Option<T>;
/// Remove and return the item before the cursor
pub fn pop_before(&mut self) -> Option<T>;

/// Insert `list` between the current element and the next
pub fn insert_list(&mut self, list: LinkedList<T>);
/// Insert `list` between the previous element and current
pub fn insert_list_before(&mut self, list: LinkedList<T>);

/// Split the list in two after the current element
/// The returned list consists of all elements following the current one.
// note: consuming the cursor is not necessary here, but it makes sense
// given the interface
pub fn split(self) -> LinkedList<T>;
/// Split the list in two before the current element
pub fn split_before(self) -> LinkedList<T>;
/// Returns the cursor position index within the `LinkedList`.
pub fn index(&self) -> Option<usize>;

/// Move to the subsequent element of the list if it exists or the empty
/// element
pub fn move_next(&mut self);
/// Move to the previous element of the list
pub fn move_prev(&mut self);

/// Get the current element
pub fn current(&mut self) -> Option<&mut T>;
/// Get the next element
pub fn peek_next(&mut self) -> Option<&mut T>;
/// Get the previous element
pub fn peek_prev(&mut self) -> Option<&mut T>;

/// Get an immutable cursor at the current element
pub fn as_cursor<'cm>(&'cm self) -> Cursor<'cm, T>;

// Now the list editing operations

/// Insert `item` after the cursor
pub fn insert_after(&mut self, item: T);
/// Insert `item` before the cursor
pub fn insert_before(&mut self, item: T);

/// Remove the current item. The new current item is the item following the
/// removed one.
pub fn remove_current(&mut self) -> Option<T>;

/// Insert `list` between the current element and the next
pub fn splice_after(&mut self, list: LinkedList<T>);
/// Insert `list` between the previous element and current
pub fn splice_before(&mut self, list: LinkedList<T>);

/// Split the list in two after the current element
/// The returned list consists of all elements following the current one.
pub fn split_after(&mut self) -> LinkedList<T>;
/// Split the list in two before the current element
pub fn split_before(&mut self) -> LinkedList<T>;
}
```
One should closely consider the lifetimes in this interface. Both `Cursor` and
`CursorMut` operate on data in their `LinkedList`. This is why, they both hold
the annotation of `'list`.

The lifetime elision for their constructors is correct as
```
pub fn cursor(&self) -> Cursor<T>
```rust
pub fn cursor_front(&self) -> Cursor<T>
```
becomes
```
pub fn cursor<'list>(&'list self) -> Cursor<'list, T>
```rust
pub fn cursor_front<'list>(&'list self) -> Cursor<'list, T>
```
which is what we would expect. (the same goes for `CursorMut`).

Since `Cursor` cannot mutate its list, `current`, `peek` and `peek_before` all
live as long as `'list`. However, in `CursorMut` we must be careful to make
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, one could produce multiple mutable references
to the same element.

Expand Down