Rc·Arc·Weak 문서의 검증 코드다.

//! Rc·Arc·Weak: strong_count의 변화, 손잡이 크기(8바이트), Rc 순환이 Drop을 막아 누수가 나는 것(Drop 횟수 0),
//! 뒤쪽 연결을 Weak로 바꾸면 Drop이 모두 도는 것, upgrade()가 None을 돌려주는 때,
//! Rc는 스레드로 보낼 수 없고(E0277) Arc는 되는 것, clippy의 arc_with_non_send_sync(기본 경고)와
//! clone_on_ref_ptr(기본 꺼짐)를 확인한다.
//! 의존성: 없음 (표준 라이브러리만)
//! 실행: python3 "_시스템/Rust 계획/rs_check.py" "Rust/3.개념집/28_rc-arc-weak_verify.rs"

use std::cell::{Cell, RefCell};
use std::rc::{Rc, Weak};
use std::sync::Arc;

thread_local! {
    static DROPS: Cell<u32> = const { Cell::new(0) };
}
fn drops() -> u32 {
    DROPS.with(|d| d.get())
}
fn reset() {
    DROPS.with(|d| d.set(0));
}

// ---------- 순환: 둘 다 Rc ----------

struct StrongNode {
    _name: &'static str,
    next: RefCell<Option<Rc<StrongNode>>>,
}
impl Drop for StrongNode {
    fn drop(&mut self) {
        DROPS.with(|d| d.set(d.get() + 1));
    }
}

// ---------- 부모는 자식을 Rc로, 자식은 부모를 Weak로 ----------

struct TreeNode {
    name: &'static str,
    parent: RefCell<Weak<TreeNode>>,
    children: RefCell<Vec<Rc<TreeNode>>>,
}
impl Drop for TreeNode {
    fn drop(&mut self) {
        DROPS.with(|d| d.set(d.get() + 1));
    }
}
fn tree_node(name: &'static str) -> Rc<TreeNode> {
    Rc::new(TreeNode {
        name,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(Vec::new()),
    })
}

fn compile_errors(code: &str) -> String {
    compile_with("rustc", "rustc", code, &[])
}

fn compile_with(tool: &str, tag: &str, code: &str, extra: &[&str]) -> String {
    let dir = std::env::temp_dir().join(format!("vr28_{}_{tag}", std::process::id()));
    std::fs::create_dir_all(&dir).expect("임시 폴더");
    let file = dir.join("snippet.rs");
    std::fs::write(&file, code).expect("쓰기");
    let out = std::process::Command::new(tool)
        .args([
            "--edition",
            "2024",
            "--crate-type=lib",
            "--emit=metadata",
            "--out-dir",
        ])
        .arg(&dir)
        .args(extra)
        .arg(&file)
        .output()
        .expect("컴파일러 실행");
    String::from_utf8_lossy(&out.stderr).into_owned()
}

fn main() {
    // 1. strong_count 추적
    let a = Rc::new(String::from("shared"));
    let mut trace = vec![Rc::strong_count(&a)];
    let b = Rc::clone(&a);
    trace.push(Rc::strong_count(&a));
    {
        let _c = Rc::clone(&a);
        trace.push(Rc::strong_count(&a));
    }
    trace.push(Rc::strong_count(&a));
    drop(b);
    trace.push(Rc::strong_count(&a));
    println!("1. strong_count: {trace:?}");
    assert_eq!(trace, vec![1, 2, 3, 2, 1]);
    // clone은 같은 힙 데이터를 가리킨다(복사 아님)
    let d = Rc::clone(&a);
    assert!(Rc::ptr_eq(&a, &d));
    assert_eq!(a.as_ptr(), d.as_ptr());
    drop(d);
    // 손잡이는 주소 한 칸뿐이다: 개수는 손잡이가 아니라 힙의 값 옆에 있다
    assert_eq!(std::mem::size_of::<Rc<String>>(), 8);
    assert_eq!(std::mem::size_of::<Arc<String>>(), 8);
    assert_eq!(std::mem::size_of::<Weak<String>>(), 8);
    println!("   size_of: Rc 8, Arc 8, Weak 8");

    // 2. Rc 순환 → 누수
    reset();
    {
        let x = Rc::new(StrongNode {
            _name: "x",
            next: RefCell::new(None),
        });
        let y = Rc::new(StrongNode {
            _name: "y",
            next: RefCell::new(Some(Rc::clone(&x))),
        });
        *x.next.borrow_mut() = Some(Rc::clone(&y)); // x → y → x
        assert_eq!((Rc::strong_count(&x), Rc::strong_count(&y)), (2, 2));
    } // 변수 x, y가 사라져도 서로를 붙잡고 있어 count가 1로 남는다
    let leaked = drops();
    println!("2. Rc 순환: 범위를 벗어난 뒤 Drop 횟수 = {leaked}");
    assert_eq!(leaked, 0);

    // 3. Weak로 끊기
    reset();
    {
        let parent = tree_node("root");
        let leaf = tree_node("leaf");
        *leaf.parent.borrow_mut() = Rc::downgrade(&parent);
        parent.children.borrow_mut().push(Rc::clone(&leaf));
        assert_eq!(Rc::strong_count(&parent), 1);
        assert_eq!(Rc::weak_count(&parent), 1);
        assert_eq!(Rc::strong_count(&leaf), 2);
        let p = leaf.parent.borrow().upgrade().map(|p| p.name);
        assert_eq!(p, Some("root"));
        assert_eq!(parent.children.borrow()[0].name, "leaf");
    }
    let freed = drops();
    println!("3. 부모←자식을 Weak로: Drop 횟수 = {freed}");
    assert_eq!(freed, 2);

    // 4. upgrade는 강한 참조가 모두 사라지면 None
    reset();
    let leaf = tree_node("leaf");
    {
        let parent = tree_node("root");
        *leaf.parent.borrow_mut() = Rc::downgrade(&parent);
        assert!(leaf.parent.borrow().upgrade().is_some());
    }
    assert!(leaf.parent.borrow().upgrade().is_none());
    assert_eq!(drops(), 1); // 부모만 해제되었다
    println!("4. 부모가 사라진 뒤 upgrade() = None");

    // 5. Arc는 스레드 사이에서 나눠 가질 수 있다
    let data = Arc::new(vec![1u64, 2, 3, 4]);
    let handles: Vec<_> = (0..4)
        .map(|i| {
            let data = Arc::clone(&data);
            std::thread::spawn(move || data[i] * 10)
        })
        .collect();
    let sum: u64 = handles
        .into_iter()
        .map(|h| h.join().expect("스레드 panic 없음"))
        .sum();
    assert_eq!(sum, 100);
    assert_eq!(Arc::strong_count(&data), 1); // 스레드가 끝나며 clone이 모두 사라졌다
    println!("5. Arc 4개 스레드: 합 {sum}, 끝난 뒤 strong_count 1");

    // 6. Rc는 Send가 아니다
    let e = compile_errors(
        "use std::rc::Rc;\npub fn f() { let a = Rc::new(5); std::thread::spawn(move || println!(\"{a}\")); }\n",
    );
    assert!(e.contains("error[E0277]"), "{e}");
    assert!(
        e.contains("`Rc<i32>` cannot be sent between threads safely"),
        "{e}"
    );
    println!("6. Rc를 스레드로 보내기: E0277");
    // Box는 안의 값에 따라 정해진다: Box<i32>는 되고 Box<Rc<i32>>는 E0277
    let ok = compile_errors(
        "pub fn f() { let b = Box::new(5); std::thread::spawn(move || println!(\"{b}\")); }\n",
    );
    assert!(ok.is_empty(), "{ok}");
    let e = compile_errors(
        "use std::rc::Rc;\npub fn f() { let b = Box::new(Rc::new(5)); std::thread::spawn(move || println!(\"{b}\")); }\n",
    );
    assert!(e.contains("error[E0277]"), "{e}");
    println!("   Box<i32>는 스레드로 보낼 수 있고 Box<Rc<i32>>는 E0277");

    // 7. Rc<T>는 &T만 준다: 안의 값을 바로 바꿀 수 없다
    let e =
        compile_errors("use std::rc::Rc;\npub fn f() { let a = Rc::new(vec![1]); a.push(2); }\n");
    assert!(e.contains("error[E0596]"), "{e}");
    assert!(
        e.contains("cannot borrow data in an `Rc` as mutable"),
        "{e}"
    );
    println!("7. Rc 안의 Vec에 push: E0596");

    // 8. clippy: 스레드로 못 보내는 값을 Arc에 담으면 기본 경고
    let e = compile_with(
        "clippy-driver",
        "arc",
        "pub fn f() -> usize { let a = std::sync::Arc::new(std::cell::RefCell::new(0)); std::sync::Arc::strong_count(&a) }\n",
        &[],
    );
    assert!(
        e.contains("`#[warn(clippy::arc_with_non_send_sync)]` on by default"),
        "{e}"
    );
    assert!(
        e.contains("if the `Arc` will not be used across threads replace it with an `Rc`"),
        "{e}"
    );
    // a.clone() 대신 Rc::clone(&a)를 강제하는 린트는 기본으로 꺼져 있다
    let snippet = "use std::rc::Rc; pub fn f(a: &Rc<i32>) -> Rc<i32> { a.clone() }\n";
    let off = compile_with("clippy-driver", "corp_off", snippet, &[]);
    // "error"가 없어야 조각이 실제로 컴파일된 것이다(호출이 실패해도 린트 이름은 안 나온다).
    assert!(
        !off.contains("error") && !off.contains("clone_on_ref_ptr"),
        "{off}"
    );
    let on = compile_with(
        "clippy-driver",
        "corp_on",
        snippet,
        &["-W", "clippy::clone_on_ref_ptr"],
    );
    assert!(
        on.contains("using `.clone()` on a ref-counted pointer"),
        "{on}"
    );
    println!("8. clippy: arc_with_non_send_sync 기본 경고, clone_on_ref_ptr는 켜야 걸린다");

    println!("ALL CHECKS PASSED");
}

#[cfg(test)]
mod tests {
    use super::*;

    /// 문서 확인 문제 C1의 순서 그대로
    #[test]
    fn card_c1_trace() {
        let a = Rc::new(5);
        let b = Rc::clone(&a);
        {
            let _c = Rc::clone(&b);
            assert_eq!(Rc::strong_count(&a), 3);
        }
        let w = Rc::downgrade(&a);
        drop(b);
        assert_eq!((Rc::strong_count(&a), Rc::weak_count(&a)), (1, 1));
        drop(a);
        assert!(w.upgrade().is_none());
    }

    #[test]
    fn weak_does_not_keep_value_alive() {
        let strong = Rc::new(5);
        let weak = Rc::downgrade(&strong);
        assert_eq!(weak.upgrade().as_deref(), Some(&5));
        drop(strong);
        assert!(weak.upgrade().is_none());
    }

    #[test]
    fn breaking_cycle_by_hand_frees_nodes() {
        reset();
        let x = Rc::new(StrongNode {
            _name: "x",
            next: RefCell::new(None),
        });
        let y = Rc::new(StrongNode {
            _name: "y",
            next: RefCell::new(Some(Rc::clone(&x))),
        });
        *x.next.borrow_mut() = Some(Rc::clone(&y));
        x.next.borrow_mut().take(); // 순환을 손으로 끊는다
        drop(x);
        drop(y);
        assert_eq!(drops(), 2);
    }
}