std::rc::Weak Mystery ^^

Hello every one,
I don't exactly know why my weak reference in that case is upgraded as None at line 18...
since the reference is supposed to be valid ...
what I miss ?
if some one can help please it's very important for me.

use dtb::*;
fn main() {
    let mut data_base = config_tree();

    data_base.branch_at_right(Some("hello !"));
    data_base.go_to_right();

    //data_base.branch_at_right(Some("too hard !"));
    //data_base.go_to_right();

    println!(
        "current previous value: {:?}",
        data_base
            .current
            .as_ref()
            .unwrap()
            .borrow_mut()
            .upgrade_previous()                      //<<<<<<<< line 18
            .ok()
    );

    // Dsiplay the tree.
    println!(
        "\n--->{:?}\nSize on ram: {} Bytes (from the current node only)\n",
        data_base,
        data_base.size_on_ram().unwrap()
    );

    println!(
        "Current value: {:?}",
        data_base.current.as_ref().unwrap().borrow().value
    );
}

pub mod dtb {
    use std::cell::RefCell;
    use std::rc::{Rc, Weak};
    type Link = Rc<RefCell<Node>>;
    type WeakLink = Weak<RefCell<Node>>;
    #[derive(Debug)]
    pub struct Node {
        pub value: Option<String>,
        pub left: Option<Link>,
        pub right: Option<Link>,
        pub prev: Option<WeakLink>,
    }

    impl Node {
        pub fn new_tree(value: Option<&str>) -> Link {
            let mut tmp: Option<String> = None;
            if let Some(str) = value {
                tmp = Some(String::from(str));
            } // just convert an Option<&str> to Option<String>.
            Rc::new(RefCell::new(Node {
                value: tmp,
                left: None,
                right: None,
                prev: None,
            }))
        }

        fn _is_root(&self) -> bool {
            if self.value == Some(String::from("root")) {
                true
            } else {
                false
            }
        }

        /// provide an upgraded previous Link.
        pub fn upgrade_previous(&mut self) -> Result<Link, &str> {
            if let Some(a) = self.prev.clone() {
                if let Some(b) = Weak::upgrade(&a) {
                    Ok(b)
                } else {
                    Err("Error, previous link not valid !")
                }
            } else {
                Err("prev = None")
            }
        }

        fn sum_all_bytes(&self) -> usize {
            let (mut tmp1, mut tmp2) = (0usize, 0usize);
            if let Some(left) = &self.left {
                tmp1 = left.borrow().sum_all_bytes();
            }
            if let Some(right) = &self.right {
                tmp2 = right.borrow().sum_all_bytes();
            }
            if let Some(a) = &self.value {
                a.len() + tmp1 + tmp2 + std::mem::size_of::<Node>()
            } else {
                0usize + tmp1 + tmp2 + std::mem::size_of::<Node>()
            }
        }
    }

    #[derive(Debug)]
    pub struct Tree {
        pub current: Option<Link>,
        pub root: Option<WeakLink>,
    }

    impl Tree {
        pub fn branch_at_right(&mut self, value: Option<&str>) {
            // if node is root.
            if let Some(a) = &self.current {
                let new_block = Node::new_tree(value);
                let mut current = Node::new_tree(None);
                if a.borrow()._is_root() {
                    // first get the current with indirection Rc<Refcell<Node>> awarness not just node.
                    current = a.borrow_mut().upgrade_previous().ok().unwrap();
                } else {
                    // first get the current with awarness
                    let prev = a.borrow_mut().upgrade_previous().ok().unwrap();
                    // then n+1 since this node is not root.
                    current = prev.borrow_mut().right.as_ref().unwrap().clone();
                }
                // update the new_block.prev
                new_block.borrow_mut().prev = Some(Rc::downgrade(&current.clone()));
                // insert the new block
                a.borrow_mut().right = Some(new_block.clone());
                println!(
                    "Insert in node :\n{:?}\n The previous node : \n{:?}\n",
                    a.borrow().right,
                    current
                );
            }
        }

        fn tree_init_step1(current_init_value: Option<&str>) -> Self {
            Self {
                current: Some(Node::new_tree(current_init_value)),
                root: None,
            }
        }

        fn tree_init_step2(&mut self) {
            if let Some(a) = &self.current {
                a.borrow_mut().prev = Some(Rc::downgrade(a));
            }
            if let None = self.root {
                if let Some(b) = &self.current {
                    self.root = Some(Rc::downgrade(b));
                }
            }
        }

        pub fn size_on_ram(&self) -> Result<usize, &str> {
            if let Some(current) = &self.current {
                Ok(current.borrow_mut().sum_all_bytes() + std::mem::size_of::<Tree>())
            } else {
                Err("Data base Tree non initialized: run fn config_tree() first !")
            }
        }

        pub fn goback_to_root(&mut self) {
            if let Some(root) = &self.root {
                self.current = Some(Weak::upgrade(root).as_ref().unwrap().clone());
            } else {
                eprintln!("Error! root Link is None");
            }
        }
        pub fn go_to_left(&mut self) {
            if let Some(current) = self.current.take() {
                self.current = current.borrow_mut().left.clone();
            } else {
                eprintln!("No left branch to move on !");
            }
        }
        pub fn go_to_right(&mut self) {
            if let Some(current) = self.current.take() {
                self.current = current.borrow_mut().right.clone();
            } else {
                eprintln!("No right branch to move on !");
            }
        }
        pub fn go_to_previous(&mut self) {
            if let Some(a) = self.current.clone() {
                if let Some(b) = a.borrow_mut().prev.clone() {
                    if let Some(prev) = Weak::upgrade(&b) {
                        self.current = Some(prev);
                    } else {
                        println!("fail to upgrade")
                    }
                }
            }
        }
    }

    pub fn config_tree() -> Tree {
        let mut db = Tree::tree_init_step1(Some("root"));
        db.tree_init_step2();
        db
    }
}

As expected the problem is too complex for being review easily on the forum at a glance,
So in the broad line:
I just downgrade an rc into a weak reference... but at the upgrade to a stronger Rc the returned value is none... which is supposed to mean that the reference was dropped before this last upgrade... ok but the ref is really still there in my tree !!!, by the way I have reintroduced the mutability in the rc indirection with an Rc<RefCell> does a .borrow_mut() call for accessing to the upgraded value can invalid the the existing reference (as borrowed aka None) even if she still there ? that my fear... if is that so any solution ?

No.

let mut current = Node::new_tree(None);
…
current = prev.borrow_mut().right.as_ref().unwrap().clone();
new_block.borrow_mut().prev = Some(Rc::downgrade(&current.clone()));
a.borrow_mut().right = Some(new_block.clone());

This looks wrong. The code is very convoluted, but it looks to me like you are "saving" Rcs in local variables, but then the Rc gets overwritten by another one, and the local copy goes out of scope as well – so there aren't any more strong references indeed.

1 Like

you are saying:

  • that you can't locally stack two distinct variable in a function,
  • downgrade one of this reference into a weak reference
  • and and ref this 2nd (into) the first
  • pass the ownership of the first to a greater life time (the tree)
    conduce to an effective drop of the weak staked in the inner function value
    and invalid the ref in the tree at upgrade out of the stack ?
    Hum... lets me thinks about that...
    thanks for answering its an fun topic but a very frustrating one believe me ^^

No, I'm not saying that. Using local variables is fine. I'm saying that if you shuffle around the pointers in a way that at some point, all strong references get dropped, then you won't be able to upgrade the corresponding weaks any more. And I suspect the current implementation is doing exactly that, unless there are more "hidden" strong Rcs not shown in the code above.

I'm not sure what exactly the data structure you want looks like – what is it you are trying to achieve at a higher level?

1 Like

thank for staying there,
what I want is a tree in which I can walk freely (right and left) an backward (prev or parent) with another structure holding the current reference position updated at each change, but mainly what I want is a direct pointer to the current location, I don't want to iterate at some point into a list of index. in fact may be unsafe code would be better in runtime cost if strong and weak referance are not reliable enough.

Definitely don't use unsafe unless you have a very thorough understanding of the language.

It doesn't look to me like you need to mutate the tree for that. You can just keep a separate strong Rc to the current node at all times; that won't lead to reference cycles. And then moving left/right or up would be as simple as current = Rc::clone(&current.left); or current = Weak::upgrade(&current.parent).unwrap(); respectively.

2 Likes

In fact I think that I complicated the problem more than enough an try to get what I already have...
Im feeling silly ^^ thanks for your help

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.