在构造函数中使用不安全和指针时,这种奇怪的行为是什么?

问题描述 投票:0回答:1

我遇到了一个奇怪的问题,程序成功或失败取决于是否将我的

unsafe
块放入构造函数中。不确定这是否是一个错误,或者我只是在后者中很幸运,或者我只是错过了一些重要的东西。

失败案例:

fn main() {
    let mut xc = Bob::default();
    let mut xp = Bob::default();
    let mut x = Bob::new(Some(&mut xc), Some(&mut xp));

    unsafe {
        (&mut *x.child.unwrap()).string = String::from("child");
        (&mut *xp.child.unwrap()).string = String::from("middle");
    }
    println!("{:?}", xc);
    println!("{:?}", x);
}

#[derive(Debug)]
struct Bob {
    child: Option<*mut Bob>,
    parent: Option<*mut Bob>,
    string: String,
}
impl Bob {
    fn new(child: Option<*mut Bob>, parent: Option<*mut Bob>) -> Bob {
        let mut this = Bob {
            child,
            parent,
            string: String::from("Hello")
        };
        if child.is_some(){
            unsafe { child.unwrap().as_mut().unwrap().parent = Some(&mut this);}
        }
        if parent.is_some(){
            unsafe { parent.unwrap().as_mut().unwrap().child = Some(&mut this);}
        }
        this
    }
}

impl Default for Bob {
    fn default() -> Bob {
        Bob {
            child: None,
            parent: None,
            string: String::from("Hello")
        }
    }
}
Bob { child: None, parent: Some(0x37b76ff7f8), string: "child" }
Bob { child: Some(0x37b76ff8b8), parent: Some(0x37b76ff8f0), string: "thread 'main' panicked at 'failed printing to stdout: Windows stdio in console mode does not support writing non-UTF-8 byte sequences', library\std\src\io\stdio.rs:1019:9
stack backtrace:
   0: std::panicking::begin_panic_handler
             at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library\std\src\panicking.rs:593
   1: core::panicking::panic_fmt
             at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library\core\src\panicking.rs:67
   2: std::io::stdio::print_to
             at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library\std\src\io\stdio.rs:1019
   3: std::io::stdio::_print
             at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225/library\std\src\io\stdio.rs:1096
   4: rust_allocators::main
             at .\src\main.rs:14
   5: core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
             at /rustc/8ede3aae28fe6e4d52b38157d7bfe0d3bceef225\library\core\src\ops\function.rs:250
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
error: process didn't exit successfully: `target\debug\rust-allocators.exe` (exit code: 0xc0000374, STATUS_HEAP_CORRUPTION)

Process finished with exit code -1073740940 (0xC0000374)

工作替代方案:

fn main() {
    let mut xc = Bob::default();
    let mut xp = Bob::default();
    let mut x = Bob::new(Some(&mut xc), Some(&mut xp));
    xp.child = Some(&mut x);
    // unsafe { Some(&mut xp as *mut Bob).unwrap().as_mut().unwrap().child = Some(&mut x);} // ALSO WORKS


    unsafe {
        (&mut *x.child.unwrap()).string = String::from("child");
        (&mut *xp.child.unwrap()).string = String::from("middle");
    }

    println!("{:?}", xc);
    println!("{:?}", x);
}

#[derive(Debug)]
struct Bob {
    child: Option<*mut Bob>,
    parent: Option<*mut Bob>,
    string: String,
}
impl Bob {
    fn new(child: Option<*mut Bob>, parent: Option<*mut Bob>) -> Bob {
        let mut this = Bob {
            child,
            parent,
            string: String::from("Hello")
        };
        if child.is_some(){
            unsafe { child.unwrap().as_mut().unwrap().parent = Some(&mut this);}
        }
        //if parent.is_some(){
        //    unsafe { parent.unwrap().as_mut().unwrap().child = Some(&mut this);}
        //}
        this
    }
}

impl Default for Bob {
    fn default() -> Bob {
        Bob {
            child: None,
            parent: None,
            string: String::from("Hello")
        }
    }
}
Bob { child: None, parent: Some(0x990ecff628), string: "child" }
Bob { child: Some(0x990ecff708), parent: Some(0x990ecff740), string: "middle" }

我希望这两种情况都能起作用。

pointers rust unsafe
1个回答
3
投票

指向

this
的指针在这里

unsafe { child.unwrap().as_mut().unwrap().parent = Some(&mut this);}

指向一个 function local 变量,一旦你从该函数返回,它就会变成悬空!之后取消引用它是未定义的行为。从函数返回一个值会移动它,因此它的地址可能会改变。

虽然 miri 换句话说,但它确实说了同样的事情:

error: Undefined Behavior: dereferencing pointer failed: alloc993 has been freed, so this pointer is dangling
  --> src/main.rs:8:9
   |
8  |         (&mut *xp.child.unwrap()).string = String::from("middle");
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: alloc993 has been freed, so this pointer is dangling
   |
   = 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
help: alloc993 was allocated here:
  --> src/main.rs:22:13
   |
22 |         let mut this = Bob {
   |             ^^^^^^^^
help: alloc993 was deallocated here:
  --> src/main.rs:34:5
   |
34 |     }
   |     ^
   = note: BACKTRACE (of the first span):
   = note: inside `main` at src/main.rs:8:9: 8:34

第二个版本有效,因为您创建了指针并且随后不移动值,因此您不会访问任何悬空指针,

parent
xc
指针仍然悬空,但您不访问它,所以这是很好。

有一种方法可以实现这一目标,我们必须确保

  1. 当我们返回时,实际值不会在内存中移动——我们可以
    Box::pin
  2. 之后无法移动 - 添加
    PhantomPinned
    ,这样它就不会实现
    Unpin

所以改成这样:

use core::marker::PhantomPinned;
use core::pin::Pin;

#[derive(Default, Debug)]
struct Bob {
    child: Option<*mut Bob>,
    parent: Option<*mut Bob>,
    string: String,
    _marker: PhantomPinned,
}

impl Bob {
    fn new(child: Option<*mut Bob>, parent: Option<*mut Bob>) -> Pin<Box<Bob>> {
        let mut this = Box::pin(Bob {
            child,
            parent,
            string: String::from("Hello"),
            _marker: PhantomPinned,
        });

        if child.is_some() {
            let this = this.as_mut();
            unsafe {
                child.unwrap().as_mut().unwrap().parent = Some(this.get_unchecked_mut());
            }
        }
        if parent.is_some() {
            let this = this.as_mut();
            unsafe {
                parent.unwrap().as_mut().unwrap().child = Some(this.get_unchecked_mut());
            }
        }
        this
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.