Today i try to use unsafe code to resolve a test, but encountered a weird problem, I found unrelated statement will affect the behavior of unsafe code. below is my code:
// Definition for singly-linked list.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct ListNode {
pub val: i32,
pub next: Option<Box<ListNode>>
}
impl ListNode {
#[inline]
fn new(val: i32) -> Self {
ListNode {
next: None,
val
}
}
}
use rand;
use std::ptr::null;
struct Solution {
list: Option<Box<ListNode>>,
ptr: *const Option<Box<ListNode>>
}
impl Solution {
fn new(head: Option<Box<ListNode>>) -> Self {
let mut s = Self {
list: head,
ptr: null(),
};
s.ptr = &s.list as *const Option<Box<ListNode>>;
s
}
fn get_random(&mut self) -> i32 {
// generate random jumps
let mut jumps: u8 = rand::random();
unsafe {
// if self.ptr is not point to the last node, update self.ptr to point next node
// else point to head
while jumps > 0 {
if (*self.ptr).as_ref().unwrap().next.is_some() {
self.ptr = &(*self.ptr).as_ref().unwrap().next as *const Option<Box<ListNode>>;
} else {
self.ptr = &self.list as *const Option<Box<ListNode>>;
}
jumps -= 1;
// if comment below statement, the result will be like:
// results: [573225736, 573225736, 573225736, 573225736]
// if uncomment this statement, the result will be like:
// results: [1, 2, 2, 1]
// obviously the later one is correct
println!("{}", jumps);
}
return (*self.ptr).as_ref().unwrap().val;
}
}
}
fn main() {
let mut s = Solution::new(Some(Box::new(ListNode{
val: 1,
next: Some(Box::new(ListNode {
val: 2,
next: Some(Box::new(ListNode {
val: 3,
next: None,
}))
}))
})));
let mut res = Vec::new();
res.push(s.get_random());
res.push(s.get_random());
res.push(s.get_random());
res.push(s.get_random());
println!("results: {:?}", res);
}
Why is the result related to a println!()
statement? It's so weird. Is there anyone can explain this for me?
unsafe
in the early stages of learning, since as witnessed here, the outcome can be disastrous. Also, related, almost mandatory reading: rust-unofficial.github.io/too-many-lists And if you tried using a reference instead and that failed, see here: stackoverflow.com/questions/32300132/… – E_net4 the curators.ptr = &s.list as *const Option<Box<ListNode>>;
are a recipe for disaster in rust, as structs regularly move around via memcpy, invalidating those pointers. – Colonel Thirty Twoptr
should point to the inner*const ListNode
not to theOption<_>
itself. SinceListNode
is behind aBox
its address should be constant, that is not the case with theOption
. – rodrigo