I am trying to implement a binary tree in unsafe Rust and it appears to be a difference between debug and release.
This code executed on debug is very likely to access wrong memory address, but if compiled in release mode it seems to be fine.
It is totally possible that I made a mistake as I am quite new to raw pointers, but having different output is really strange.
Is my different output really a sign of wrong memory access? Is that expected when working with unsafe Rust? It is the sign of code smell somehow?
In debug mode, the output on my machine is:
constructing tree
5
constructed
0.000000000000000000000000000000000000000000001
value added
In release mode, the output on my machine is:
constructing tree
5
constructed
5
value added
Here the code, reduced as much as I could.
use std::ptr;
struct Node {
value: f32,
node_left: *mut Node,
node_right: *mut Node,
}
impl Node {
pub fn from_value(value: f32) -> Node {
println!("{}", value);
Node {
value: value,
node_left: ptr::null_mut(),
node_right: ptr::null_mut(),
}
}
fn get_value(&self) -> f32 {
self.value
}
}
pub struct BinaryTree {
root: *mut Node,
}
impl BinaryTree {
pub fn from_value(value: f32) -> BinaryTree {
let mut node = &mut Node::from_value(value);
BinaryTree { root: node }
}
pub fn add(&mut self, value: f32) {
println!("{}", unsafe { self.root.as_mut() }.unwrap().get_value());
}
}
fn main() {
println!("constructing tree");
let mut x = BinaryTree::from_value(5.0f32);
println!("constructed");
x.add(2f32);
println!("value added");
}
I ran this on Ubuntu 18.04 in an Oracle VM using Rust 1.32.0.
In BinaryTree::from_value
, you're creating a new Node
and then storing a pointer to it. However, the Node
is allocated on the stack, and is dropped before you call BinaryTree::add
. Because you're using pointers and unsafe
instead of references, the Rust compiler can't warn you about lifetime issues like these.
As to why this fails in debug mode but works in release mode, it may be due to an optimization that's only enabled for release mode.