Skip to content

Commit

Permalink
Merge pull request #2847 from LukasKalbertodt/fix-linked-list-cursor-…
Browse files Browse the repository at this point in the history
…names

Fix linked list cursor names
  • Loading branch information
Mark-Simulacrum committed Jan 14, 2020
2 parents 81c6deb + 772fb67 commit 3df38f6
Showing 1 changed file with 103 additions and 94 deletions.
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

0 comments on commit 3df38f6

Please sign in to comment.