can*_*eel 0 pointers rust raw-pointer
在实现链接列表时,我正在努力学习原始指针。一段简单的代码为我带来了意想不到的结果,因此我很难找到任何解释:
use std::cmp::PartialEq;
use std::default::Default;
use std::ptr;
pub struct LinkedListElement<T> {
pub data: T,
pub next: *mut LinkedListElement<T>,
}
pub struct LinkedList<T> {
head: *mut LinkedListElement<T>,
}
impl<T: PartialEq> LinkedListElement<T> {
pub fn new(elem: T, next: Option<*mut LinkedListElement<T>>) -> LinkedListElement<T> {
let mut_ptr = match next {
Some(t) => t,
None => ptr::null_mut(),
};
let new_elem = LinkedListElement {
data: elem,
next: mut_ptr,
};
if !mut_ptr.is_null() {
println!(
"post create ll mut ptr: {:p}, post create ll mut ptr next {:p}",
mut_ptr,
unsafe { (*mut_ptr).next }
);
}
new_elem
}
}
impl<T: PartialEq + Default> LinkedList<T> {
pub fn new(elem: T) -> LinkedList<T> {
LinkedList {
head: &mut LinkedListElement::new(elem, None),
}
}
pub fn insert(&mut self, elem: T) {
println!("head: {:p} . next: {:p}", self.head, unsafe {
(*self.head).next
});
let next = Some(self.head);
let mut ll_elem = LinkedListElement::new(elem, next);
println!(
"before pointer head: {:p}. before pointer next {:p}",
self.head,
unsafe { (*self.head).next }
);
let ll_elem_ptr = &mut ll_elem as *mut LinkedListElement<T>;
self.head = ll_elem_ptr;
}
}
fn main() {
let elem: i32 = 32;
let second_elem: i32 = 64;
let third_elem: i32 = 72;
let mut list = LinkedList::new(elem);
list.insert(second_elem);
list.insert(third_elem);
}
Run Code Online (Sandbox Code Playgroud)
(游乐场)
这段代码为我提供了以下输出:
head: 0x7ffe163275e8 . next: 0x0
post create ll mut ptr: 0x7ffe163275e8, post create ll mut ptr next 0x0
before pointer head: 0x7ffe163275e8. before pointer next 0x0
head: 0x7ffe16327560 . next: 0x7ffe163275e8
post create ll mut ptr: 0x7ffe16327560, post create ll mut ptr next 0x7ffe163275e8
before pointer head: 0x7ffe16327560. before pointer next 0x7ffe16327560
Run Code Online (Sandbox Code Playgroud)
对于前两个元素,代码的行为符合预期:它创建一个以空指针为下一个元素的元素。这是添加第二个元素后的状态:
{
head: {
elem: 64,
next: {
elem: 32,
next: nullptr
}
}
}
64 -> 32 -> null
Run Code Online (Sandbox Code Playgroud)
当添加第三个元素时,事情变得很奇怪,并且链表变成了这样的东西:
{
head: {
elem: 72,
next: {
elem: 72,
next: {
elem: 72,
next: ...
}
}
}
}
72 -> 72 -> 72 -> ...
Run Code Online (Sandbox Code Playgroud)
似乎链表元素的next字段开始指向该元素本身。
我调试了该LinkedListElement::new方法,发现应该从该方法返回正确的元素:
{
elem: 72,
next: {
elem: 64,
next: {
elem: 32,
next: nullptr
}
}
}
Run Code Online (Sandbox Code Playgroud)
由于某种原因,返回到LinkedList::insert方法之后,甚至在self.head重新分配之前,的内容都会LinkedList self“损坏”。
我知道在Rust中使用原始指针不是习惯用法,但我仍然想学习它们。
恭喜,您已经成功证明了Rust为何必须首先存在:程序员编写内存不安全的代码。
首先,请阅读为什么使用安全Rust时不允许这样做:
TL; DR:LinkedListElement移动时更改的内存地址。从函数返回值时(其他时间)发生移动。通过使用原始指针,您已经破坏了借用检查器,并且没有从编译器获得有用的反馈。
其次,请阅读“使用太多的链表来学习Rust”。无论出于何种原因,程序员都认为链表很“简单”,是学习语言的好方法。在Rust中,这通常是不正确的,Rust中的内存安全性至关重要。
TL; DR:您可以使用A Box在堆上分配内存。当指向它的指针移动时,该内存地址将不会改变。您需要确保在链表超出范围时适当释放指针,以防止内存泄漏。
也可以看看: