Skip to content

Stack overflow at runtime when using Rc<RefCell<T>> in cycle detection algorithm #132263

Closed
@Ayanekoji

Description

@Ayanekoji

I tried this code:

use std::{cell::RefCell, rc::Rc};

#[derive(Debug)]
struct CycleNode {
    pos: i32,
    next: Option<Rc<RefCell<CycleNode>>>,
}

impl CycleNode {
    fn new(pos: i32) -> Rc<RefCell<Self>> {
        Rc::new(RefCell::new(CycleNode { pos, next: None }))
    }
}

pub fn detect_cycle(head: Option<Rc<RefCell<CycleNode>>>) -> Option<Rc<RefCell<CycleNode>>> {
        if head.is_none() {
            return None;
        }

        let mut fast = head.clone();
        let mut slow = head.clone();

        while let (Some(fast_node), Some(slow_node)) = (fast, slow) {
            if fast_node.borrow().next.is_none()
                || fast_node
                    .borrow()
                    .next
                    .as_ref()
                    .unwrap()
                    .borrow()
                    .next
                    .is_none()
            {
                return None;
            }

            fast = fast_node
                .borrow()
                .next
                .as_ref()
                .unwrap()
                .borrow()
                .next
                .clone();
            slow = slow_node.borrow().next.clone();

            if let (Some(f), Some(s)) = (&fast, &slow) {
                if Rc::ptr_eq(f, s) {
                    let mut entry = head.clone();

                    while let (Some(entry_node), Some(fast_node)) = (&entry.clone(), &fast.clone())
                    {
                        if Rc::ptr_eq(entry_node, fast_node) {
                            return entry;
                        }

                        entry = entry_node.borrow().next.clone();
                        fast = fast_node.borrow().next.clone();
                    }
                }
            }
        }

        None
    }
     
fn main() {
   let nodes: Vec<Rc<RefCell<CycleNode>>> = (0..5)
        .map(|pos| CycleNode::new(rand::thread_rng().gen_range(1..10), pos))
        .collect();
    for i in 0..nodes.len() - 1 {
        nodes[i].borrow_mut().next = Some(nodes[i + 1].clone());
    }
    nodes[4].borrow_mut().next = Some(nodes[2].clone());
    let head = Some(nodes[0].clone());
    let result = Solution::detect_cycle(head);
    assert_eq!(result,Some(nodes[2].clone()));
print!("success");
}

I expected to see this happen: success

Instead, this happened:

wind@Unreal:~/Learning_Projects/Rust/leetcode$ cargo build
   Compiling leetcode v0.1.0 (/home/wind/Learning_Projects/Rust/leetcode)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.59s
wind@Unreal:~/Learning_Projects/Rust/leetcode$ cargo run
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.03s
     Running `/home/wind/Learning_Projects/Rust/target/debug/leetcode`

thread 'main' has overflowed its stack
fatal runtime error: stack overflow

Meta

rustc --version --verbose:

<version> wind@Unreal:~/Learning_Projects/Rust/leetcode$ rustc --version
rustc 1.82.0 (f6e511eec 2024-10-15)
Backtrace

<backtrace>

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-discussionCategory: Discussion or questions that doesn't represent real issues.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions