For purely didactic purposes I'm dealing with a singly linked list.
I'm aware that working with this particular type of data structure in Rust
requires a certain understanding of many core concepts (beautifully presented
in "Learning Rust With Entirely Too Many Linked Lists" as well as in this
forum) but still I cannot see what I'm missing to reach a working solution.
The problem is a classic: find a loop in a singly linked list.
The list is defined as:
use std::cell::Ref;
use std::cell::RefCell;
use std::rc::Rc;
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct ListNode {
pub val: i32,
pub next: Option<Rc<RefCell<ListNode>>>,
}
The test I would like to pass is:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn has_cycle() {
// Define a list with a loop (n2 -> n0).
let n0 = Rc::new(RefCell::new(ListNode { val: 0, next: None }));
let n1 = Rc::new(RefCell::new(ListNode { val: 0, next: None }));
let n2 = Rc::new(RefCell::new(ListNode { val: 0, next: None }));
n0.borrow_mut().next = Some(Rc::clone(&n1));
n1.borrow_mut().next = Some(Rc::clone(&n2));
n2.borrow_mut().next = Some(Rc::clone(&n0));
let list = Some(n0);
assert_eq!(super::has_cycle(list), true);
}
}
I was thinking of solving the problem with 2 "pointers": a slow one that
moves of 1 node at the time, and a fast one, that moves of 2 nodes at the
time. If there is a loop the 2 pointers will eventually meet, otherwise a None
is found.
I tried to define a "single" iterator as follows; please note that the type
definitions are explicit only for a better/personal understanding and that
the implementation is just partial:
pub fn has_cycle(head: Option<Rc<RefCell<ListNode>>>) -> bool {
let mut slow: Option<Rc<RefCell<ListNode>>> = head;
loop {
let nxt: Rc<RefCell<ListNode>> = slow.unwrap();
let l: Ref<ListNode> = nxt.borrow();
if let Some(n) = &l.next {
let next: Rc<RefCell<ListNode>> = Rc::clone(n);
slow = Some(next)
} else {
break;
}
}
// ... a lot of logic is missing
false
}
This compile correctly but clearly I cannot define a second "iterator" after
slow
in the same way. So I considered using as_ref
:
let mut slow: Option<&Rc<RefCell<ListNode>>> = head.as_ref();
let mut fast: Option<&Rc<RefCell<ListNode>>> = head.as_ref();
But I ended up not being able to update the next
in the loop. I also tried
to use a match
:
loop {
match slow {
None => break,
Some(n) => {
let n: Ref<ListNode> = n.borrow();
slow = n.next.as_ref();
}
}
}
but this obviously breaks the borrowing rules:
slow = n.next.as_ref();
^ borrowed value does not live long enough
So my question(s):
- am I too naive in tackling this problem with this approach?
- is my understanding of
Rc
andRefCell
"misplaced" in this context?
Thank you so much in advance.