2 Boxes allotted have same address

#[derive(Eq, PartialEq, Debug, Copy, Clone)]
pub struct Entry {
    key: u32,
    value: u32,
}

impl Entry {
    pub fn new(key: u32, value: u32) -> Self {
        Self {
            key,
            value,
        }
    }
} 

#[derive(PartialEq, Eq, Debug)]
struct Node{
    e: Entry,
    next: Option<Box<Node>>,
}

impl Node {
    fn new(e: Entry) -> Box<Self> {
        Box::from(Self {
            e,
            next: None,
        })
    }
}

struct Map { }

impl Map {

    pub fn set(&mut self, e: Entry) {
        let mut new = Node::new(e);
        let new_ptr = &mut *new as *mut Node;
        println!("NEW_PTR: {:?}", new_ptr);
    }
}

fn main() {
    let mut m = Map{};
    let g = Entry::new(45, 54);
    let k = Entry::new(54, 45);
    m.set(k);
    m.set(g);
}

Playground link

When I run the above program, I always get the same value printed for NEW_PTR, no matter how many times I run it. I expect them to be different as I am creating 2 different Boxes in the heap and they should have different addresses. I am not able to figure out the problem here. Am I missing something here?
Thanks.

The Node you create inside the set method is dropped at the end of the method, i.e. the address is being made available again afterwards. Since you call the same method twice in a row, seeing the same address being used is not surprising, at all.

2 Likes

Thanks. I get the problem. But this is just the simplified version of the code. In the full version, I am storing the addresses in a vector. And these addresses being the same is causing problems. I don't know how to prevent deallocation of these items.

Full code

Any help is appreciated. Thanks.

Use Box::into_raw instead of pointer casting magic. You should be seeing an error, then.

2 Likes

Thanks. I was able to solve the problem by moving variable declaration inside the loop, and making sure that new variable is declared in every iteration. So avoiding the errors like, using moved value.
Thanks once again.

But what actually happened to the variables when I used the "pointer casting magic"? Why were they dropped?

The box neither returned nor stored externally, so it got dropped at the end of the function. And most allocators reuse the previously freed space for next allocation for fast lookup.

2 Likes

I see. I thought that storing the address of the box is somehow same as having a reference to it. And therefore thought that the box being dropped is not possible.

pub fn set(&mut self, e: Entry) {
    let h = e.hash() % self.size;
    println!("hash: {:?}", h);
    let curr = &self.list[h];
    let mut old_ptr = curr.load(Ordering::SeqCst);
    loop {
        let mut new = Node::new(e);
        if !old_ptr.is_null() {
            unsafe {
                new.next =  Some(Box::from_raw(old_ptr));
            }                
        }
        println!("NEW: {:?}", new);
        let new_ptr = Box::into_raw(new);
        println!("NEW_PTR: {:?}", new_ptr);
        println!("OLD_PTR: {:?}", old_ptr);
        let res = curr.compare_and_swap(old_ptr, new_ptr, Ordering::SeqCst);
        println!("RES: {:?}", res);
        if res == old_ptr {
            return;
        }
        old_ptr = res;
    }
}

This code works. But honestly I don't see how the reference to the variable is maintained here.

You were storing a raw pointer. A raw pointer is a type that fundamentally has no lifetime associated with it, so nothing stops you from keeping it around after the box is dropped.

As for why it works with Box::into_raw, that method consumes the box and produces a pointer, but it doesn't deallocate the memory. The only way to have the memory deallocated later is to turn it back into a box with Box::from_raw and drop that box. Basically the purpose of the function is to manually take over the allocation deallocation process.

In fact in your case you have a memory leak, because you don't have Box::from_raw anywhere in the code.

2 Likes

^^This???

Oh I missed that entirely. That will indeed cause whatever memory old_ptr points to to be deallocated once new.next is dropped (and the assignment to new.next would drop the box previously in that field). However if you create the new box before destroying the old, of course they will have distinct addresses.

1 Like