आज मैं एक परीक्षण को हल करने के लिए असुरक्षित कोड का उपयोग करने की कोशिश करता हूं, लेकिन एक अजीब समस्या का सामना करना पड़ा, मैंने पाया कि असंबंधित कथन असुरक्षित कोड के व्यवहार को प्रभावित करेगा। नीचे मेरा कोड है:

// 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);
}

परिणाम println!() कथन से संबंधित क्यों है? यह बहुत अजीब है। क्या कोई मेरे लिए यह समझा सकता है?

0
wangjun 16 अगस्त 2021, 15:41
यह अपरिभाषित व्यवहार की तरह दिखता है। प्रति उत्तर नहीं है, लेकिन सीखने के शुरुआती चरणों में unsafe का उपयोग करने की सलाह दी जाती है, क्योंकि जैसा कि यहां देखा गया है, परिणाम विनाशकारी हो सकता है। साथ ही, संबंधित, लगभग अनिवार्य पठन: rust-unofficial.github.io/too-many -सूचियांऔर यदि आपने इसके बजाय किसी संदर्भ का उपयोग करने का प्रयास किया और वह विफल हो गया, तो यहां देखें: stackoverflow.com/questions/32300132/…
 – 
E_net4 the candidate supporter
16 अगस्त 2021, 15:57
2
आंतरिक पॉइंटर्स जैसे s.ptr = &s.list as *const Option<Box<ListNode>>; जंग में आपदा के लिए एक नुस्खा हैं, क्योंकि स्ट्रक्चर नियमित रूप से memcpy के माध्यम से घूमते हैं, उन पॉइंटर्स को अमान्य कर देते हैं।
 – 
Colonel Thirty Two
16 अगस्त 2021, 16:02
संभवतः आपका ptr आंतरिक *const ListNode को Option<_> की ओर इंगित करना चाहिए। चूँकि ListNode एक Box के पीछे है, इसका पता स्थिर होना चाहिए, Option के साथ ऐसा नहीं है।
 – 
rodrigo
16 अगस्त 2021, 17:06

1 उत्तर

सबसे बढ़िया उत्तर

असुरक्षित उपयोग करते समय अपरिभाषित व्यवहार और अन्य समस्याओं की जांच के लिए आप MIRI टूल का उपयोग कर सकते हैं। मुझे लगता है कि यह सभी समस्याओं को नहीं पकड़ेगा, फिर भी यह अभी भी अविश्वसनीय रूप से उपयोगी है। वर्तमान में यह केवल रात के समय उपलब्ध है, लेकिन इसका उपयोग करना बहुत आसान है जंग का खेल का मैदान:

error: Undefined Behavior: pointer to alloc1603 was dereferenced after this allocation got freed
  --> src/main.rs:45:20
   |
45 |                 if (*self.ptr).as_ref().unwrap().next.is_some() {
   |                    ^^^^^^^^^^^ pointer to alloc1603 was dereferenced after this allocation got freed
   |
   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information

जैसा कि कुछ टिप्पणियों ने सुझाव दिया है कि आप पॉइंटर को Box के बजाय Option (जो स्थानांतरित कर सकते हैं) में संग्रहीत कर रहे हैं जो नहीं कर सकता। अगर आप बदलते हैं

    ptr: *const Option<Box<ListNode>>

प्रति

    ptr: Option<*const ListNode>,

आप समस्या से बचेंगे। इसे MIRI द्वारा सत्यापित किया जा सकता है, जो अब एक त्रुटि संदेश नहीं देता है:

// 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: Option<*const ListNode>,
}

impl Solution {
    fn new(head: Option<Box<ListNode>>) -> Self {
        let p = match &head {
            None => None,
            Some(h) => Some(&**h as *const ListNode),
        };

        let mut s = Self { list: head, ptr: p };
        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 let Some(p) = self.ptr {
                    self.ptr = match &(*p).next {
                        None => Some(&**self.list.as_ref().unwrap() as *const ListNode),
                        Some(n) => Some(&**n as *const 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.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);
}

पीएस: आपका कोड मानता है कि new() को पास की गई सूची कभी भी None नहीं होगी और उस स्थिति में बुरी चीजें हो सकती हैं :)

1
Svetlin Zarev 16 अगस्त 2021, 19:43
&**h as *const ListNode के बजाय मैं Box::as_ref(h) as *const ListNode करना पसंद करता हूं, इस तरह मैंने ऑटो डीरेफरेंस को यह तय करने दिया कि कितने * का उपयोग करना है।
 – 
rodrigo
16 अगस्त 2021, 19:39
ज़रेव बहुत बहुत धन्यवाद, मुझे लगता है कि मुझे *const Option<Box<ListNode>> और Option<*const ListNode> के बीच का अंतर मिला है, मैंने *const ListNode के साथ पुनः प्रयास किया है, यह बहुत अच्छी तरह से काम करता है, क्योंकि फ़ंक्शन का इनपुट कभी नहीं होगा None। लेकिन क्या यह समाधान स्थिर है? क्या ऐसी कोई स्थिति है जिसमें लिंक की गई सूची किसी अन्य स्थान पर चली जाएगी?
 – 
wangjun
17 अगस्त 2021, 04:16