All of lore.kernel.org
 help / color / mirror / Atom feed
From: Boqun Feng <boqun.feng@gmail.com>
To: Alice Ryhl <aliceryhl@google.com>
Cc: "Miguel Ojeda" <ojeda@kernel.org>,
	"Alex Gaynor" <alex.gaynor@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <benno.lossin@proton.me>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Trevor Gross" <tmgross@umich.edu>,
	rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH v4 2/2] rust: list: make the cursor point between elements
Date: Thu, 23 Jan 2025 13:45:32 -0800	[thread overview]
Message-ID: <Z5K4fPgPBNOJaJGl@boqun-archlinux> (raw)
In-Reply-To: <20250123-cursor-between-v4-2-cb06698db94c@google.com>

On Thu, Jan 23, 2025 at 11:01:09AM +0000, Alice Ryhl wrote:
[...]
>  
>  /// A cursor into a [`List`].
>  ///
> +/// A cursor always rests between two elements in the list. This means that a cursor has a previous
> +/// and next element, but no current element. It also means that it's possible to have a cursor
> +/// into an empty list.
> +///
> +/// # Examples
> +///
> +/// ```
> +/// use kernel::prelude::*;
> +/// use kernel::list::{List, ListArc, ListLinks};
> +///
> +/// #[pin_data]
> +/// struct ListItem {
> +///     value: u32,
> +///     #[pin]
> +///     links: ListLinks,
> +/// }
> +///
> +/// impl ListItem {
> +///     fn new(value: u32) -> Result<ListArc<Self>> {
> +///         ListArc::pin_init(try_pin_init!(Self {
> +///             value,
> +///             links <- ListLinks::new(),
> +///         }), GFP_KERNEL)
> +///     }
> +/// }
> +///
> +/// kernel::list::impl_has_list_links! {
> +///     impl HasListLinks<0> for ListItem { self.links }
> +/// }
> +/// kernel::list::impl_list_arc_safe! {
> +///     impl ListArcSafe<0> for ListItem { untracked; }
> +/// }
> +/// kernel::list::impl_list_item! {
> +///     impl ListItem<0> for ListItem { using ListLinks; }
> +/// }
> +///
> +/// // Use a cursor to remove the first element with the given value.
> +/// fn remove_first(list: &mut List<ListItem>, value: u32) -> Option<ListArc<ListItem>> {
> +///     let mut cursor = list.cursor_front();
> +///     while let Some(next) = cursor.peek_next() {
> +///         if next.value == value {
> +///             return Some(next.remove());
> +///         }
> +///         cursor.move_next();
> +///     }
> +///     None
> +/// }
> +///
> +/// // Use a cursor to remove all elements with the given value. The removed elements are moved to
> +/// // a new list.
> +/// fn remove_all(list: &mut List<ListItem>, value: u32) -> List<ListItem> {
> +///     let mut out = List::new();
> +///     let mut cursor = list.cursor_front();
> +///     while let Some(next) = cursor.peek_next() {
> +///         if next.value == value {
> +///             out.push_back(next.remove());
> +///         } else {
> +///             cursor.move_next();
> +///         }
> +///     }
> +///     out
> +/// }
> +///
> +/// // Use a cursor to insert a value at a specific index. Returns an error if the index is out of
> +/// // bounds.
> +/// fn insert_at(list: &mut List<ListItem>, new: ListArc<ListItem>, idx: usize) -> Result {
> +///     let mut cursor = list.cursor_front();
> +///     for _ in 0..idx {
> +///         if !cursor.move_next() {
> +///             return Err(EINVAL);
> +///         }
> +///     }
> +///     cursor.insert_next(new);
> +///     Ok(())
> +/// }
> +///
> +/// // Merge two sorted lists into a single sorted list.
> +/// fn merge_sorted(list: &mut List<ListItem>, merge: List<ListItem>) {
> +///     let mut cursor = list.cursor_front();
> +///     for to_insert in merge {
> +///         while let Some(next) = cursor.peek_next() {
> +///             if to_insert.value < next.value {
> +///                 break;
> +///             }
> +///             cursor.move_next();
> +///         }
> +///         cursor.insert_prev(to_insert);
> +///     }
> +/// }
> +///
> +/// let mut list = List::new();
> +/// list.push_back(ListItem::new(14)?);
> +/// list.push_back(ListItem::new(12)?);
> +/// list.push_back(ListItem::new(10)?);
> +/// list.push_back(ListItem::new(12)?);
> +/// list.push_back(ListItem::new(14)?);
> +/// assert_eq!(remove_all(&mut list, 12).iter().count(), 2);
> +/// // [14, 10, 14]
> +/// assert!(remove_first(&mut list, 14).is_some());
> +/// // [10, 14]
> +/// insert_at(&mut list, ListItem::new(12)?, 1)?;
> +/// // [10, 12, 14]
> +///
> +/// let mut list2 = List::new();
> +/// list.push_back(ListItem::new(11)?);
> +/// list.push_back(ListItem::new(13)?);

These need to be `list2` instead of `list`, otherwise the doctest will
fail.

> +/// merge_sorted(&mut list, list2);
> +///
> +/// let mut items = list.into_iter();
> +/// assert_eq!(items.next().unwrap().value, 10);
> +/// assert_eq!(items.next().unwrap().value, 11);
> +/// assert_eq!(items.next().unwrap().value, 12);
> +/// assert_eq!(items.next().unwrap().value, 13);
> +/// assert_eq!(items.next().unwrap().value, 14);
> +/// assert!(items.next().is_none());
> +/// # Result::<(), Error>::Ok(())
> +/// ```
> +///
>  /// # Invariants
>  ///
> -/// The `current` pointer points a value in `list`.
> +/// The `next` pointer is null or points a value in `list`.
>  pub struct Cursor<'a, T: ?Sized + ListItem<ID>, const ID: u64 = 0> {
> -    current: *mut ListLinksFields,
>      list: &'a mut List<T, ID>,
> +    /// Points at the element after this cursor, or null if the cursor is after the last element.
> +    next: *mut ListLinksFields,
>  }
>  
[...]

While we are at it, could you also add a doc test that uses
`cursor_back()` and `move_prev()`? Something like below, that would
show (and at least test a bit on) the API for another direction.

With all these, feel free to add:

Reviewed-by: Boqun Feng <boqun.feng@gmail.com>

Regards,
Boqun

------>8
diff --git a/rust/kernel/list.rs b/rust/kernel/list.rs
index b83016e5bcd9..2d38fbaf94e3 100644
--- a/rust/kernel/list.rs
+++ b/rust/kernel/list.rs
@@ -625,6 +625,18 @@ fn next(&mut self) -> Option<ArcBorrow<'a, T>> {
 ///     None
 /// }
 ///
+/// // Use a cursor to remove the last element with the given value.
+/// fn remove_last(list: &mut List<ListItem>, value: u32) -> Option<ListArc<ListItem>> {
+///     let mut cursor = list.cursor_back();
+///     while let Some(prev) = cursor.peek_prev() {
+///         if prev.value == value {
+///             return Some(prev.remove());
+///         }
+///         cursor.move_prev();
+///     }
+///     None
+/// }
+///
 /// // Use a cursor to remove all elements with the given value. The removed elements are moved to
 /// // a new list.
 /// fn remove_all(list: &mut List<ListItem>, value: u32) -> List<ListItem> {
@@ -672,12 +684,15 @@ fn next(&mut self) -> Option<ArcBorrow<'a, T>> {
 /// list.push_back(ListItem::new(12)?);
 /// list.push_back(ListItem::new(10)?);
 /// list.push_back(ListItem::new(12)?);
+/// list.push_back(ListItem::new(15)?);
 /// list.push_back(ListItem::new(14)?);
 /// assert_eq!(remove_all(&mut list, 12).iter().count(), 2);
-/// // [14, 10, 14]
+/// // [14, 10, 15, 14]
 /// assert!(remove_first(&mut list, 14).is_some());
+/// // [10, 15, 14]
+/// assert!(remove_last(&mut list, 15).is_some());
 /// // [10, 14]
 /// insert_at(&mut list, ListItem::new(12)?, 1)?;
 /// // [10, 12, 14]
 ///
 /// let mut list2 = List::new();

  reply	other threads:[~2025-01-23 21:46 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-01-23 11:01 [PATCH v4 0/2] Make the Rust linked list cursor point between elements Alice Ryhl
2025-01-23 11:01 ` [PATCH v4 1/2] rust: list: extract common code for insertion Alice Ryhl
2025-01-23 11:01 ` [PATCH v4 2/2] rust: list: make the cursor point between elements Alice Ryhl
2025-01-23 21:45   ` Boqun Feng [this message]
2025-01-27 10:35     ` Alice Ryhl

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=Z5K4fPgPBNOJaJGl@boqun-archlinux \
    --to=boqun.feng@gmail.com \
    --cc=a.hindborg@kernel.org \
    --cc=alex.gaynor@gmail.com \
    --cc=aliceryhl@google.com \
    --cc=benno.lossin@proton.me \
    --cc=bjorn3_gh@protonmail.com \
    --cc=gary@garyguo.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=tmgross@umich.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.