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();
next prev parent 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).