2

I learn how to write linklists from https://rust-unofficial.github.io/too-many-lists/sixth-random-bits.html, and I try to write an AvlTree with what I learned from it , but when I write a bit code, I'm stuck with this exception:

hello-rust(23862,0x204488600) malloc: *** error for object 0x600003b79140: pointer being freed was not allocated hello-rust(23862,0x204488600) malloc: *** set a breakpoint in malloc_error_break to debug 

I dont't know where should I change the code to make it right. Here's my all code:

use std::ptr::NonNull; type Link<T> = Option<NonNull<Node<T>>>; #[derive(Debug)] pub struct Node<T> { elem: T, parent: Link<T>, left: Link<T>, right: Link<T>, bf: i8, } impl<T> Node<T> { pub fn new_with_elem(elem: T) -> Node<T> { Node { elem, parent: None, left: None, right: None, bf: 0, } } } #[derive(Debug)] pub struct AvlTree<T> { root: Link<T>, count: usize, } impl<T: PartialEq + Eq + std::cmp::PartialOrd + Clone+std::fmt::Display> AvlTree<T> { pub fn new() -> AvlTree<T> { AvlTree { root: None, count: 0 } } pub fn insert(&mut self, elem: T) { let elem_clone = elem.clone(); let mut new_node = Node::new_with_elem(elem); if self.count == 0 { self.root = new_link_with_node(new_node); self.count += 1; return; } let mut bf = 0; let mut parent: Link<T> = None; let mut n = self.root; while n.is_some() { n.map(|node| { unsafe { println!("elem={}", &elem_clone); let box_node = Box::from_raw(node.clone().as_ptr()); if elem_clone == box_node.elem { return; } parent = n; bf <<= 1; if elem_clone < box_node.elem { n = box_node.left; } else { n = box_node.right; bf |= 1; } } }); } } } impl<T: std::fmt::Display> std::fmt::Display for Node<T> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!(fmt, " elem:{},bf:{} | ", self.elem, self.bf) } } impl<T: std::fmt::Display> std::fmt::Display for AvlTree<T> { fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { if self.root.is_none() { write!(fmt, " empty tree! ") } else { show_node(&self.root); write!(fmt, " print over ") } } } fn show_node<T: std::fmt::Display>(node: &Link<T>) { node.map(|n| { unsafe { let box_node = Box::from_raw(n.as_ptr()); println!("{}", box_node); if box_node.left.is_some() { show_node(&box_node.left) } if box_node.right.is_some() { show_node(&box_node.right) } } }); } fn new_link_with_node<T: PartialEq + Eq + std::cmp::PartialOrd + Clone>(node: Node<T>) -> Link<T> { NonNull::new(Box::into_raw(Box::new(node))) } 
1
  • There's no main here. Commented Aug 16, 2022 at 13:21

1 Answer 1

4

The issue very likely comes from the fact that you are misusing Rust boxes. Boxes are, from an ownership point of view, owned pointers, that is, a pointer with the additional constraint that whomever owns it also owns the pointed data. This means that, whenever you create a box from an existing pointer, you "take" that data (which is why it's an unsafe operation). This implies that, whenever the box is dropped (at the end of the scope), the pointed data is also dropped.

In particular, this means that your show_node function actually "destroys" any node it takes as an argument (and by destroying I mean something much worse than freeing: it leads to UB).

If you are a beginner with Rust, I would advice against using unsafe blocks. Instead, you should try to solve your issue in a safe way, which is probably the idiomatic way of doing.

Sign up to request clarification or add additional context in comments.

5 Comments

Yes, show_code will take ownership, but I haven't use it. I use this way of Box is learned from the link I post. I don't know why the author can run it right.Could you please give me show code to slove it? I still have no idea
@ggl The only places where the article in your link use from_raw are the pop methods, which move values out of the list -something that show_node should not be doing.
@ggl unfortunately, I can't "solve" your lack of knowledge about ownership in Rust. Fortunately enough, you can. (Re-)Read the Rust book's section about it, it will probably help.
emmmm, I might understand the reason of the problem it says .In the while I use Box::from_raw and it will be destroyed after a circle. Meanwhile, T is also be destroyed! When main function ends, it will try to free tree which its nodes own the Box , and it cause double free. Is it the reason?
@ggl more or less, it's that. At the end of each iteration, the box will be dropped, which will also free the allocated value (and drop the pointed value). This means that your NonNull points to a freed area, which I think is UB. Anyways, that's the issue.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.