如果您對我漫不經心地談論我不明白的東西不感興趣,請查看底部的 TLDR :)。
我正在嘗試在 Rust 中實作 A-star 尋路演算法。只是一個基本版本。我不想使用Rc并且我的節點存盤*const(原始指標)到前一個Node(prev: const* Node),以便我可以檢索最短路徑(解決方案)。
struct Node {
position: Position,
// NonNull also works I guess
previous: Option<*const Node>
}
我也在使用PriorityQueue(crate link)存盤我的節點。我對記憶體管理和低級內容的了解非常有限。
好的,現在我將首先談談我的程式內部發生了什么,以更好地闡明我在問什么。
據我了解,我的節點被存盤/移動到PriorityQueue實際上是一個HashMap.
PriorityQueue-> ...-> Vec<Bucket<K, V>>->Bucket { hash: HashValue, key: K, value: V }
所以它們被存盤在 上heap,但問題是我的節點首先被創建stack(我從不使用 堆分配它們Box,或者它們的生命周期只是系結到我猜的堆疊?)。
然后他們的previous欄位被分配一個指向前一個節點的原始指標*const Node,然后他們被移動/推入vector從函式回傳的那個(現在它們在堆上)。
節點:
stack->Vec
回傳的函式代碼Vec:
fn neighbours(&self, maze: &Maze) -> Vec<Node> {
let offset_x: [isize; 8] = [-1, -1, 0, 1, 1, 1, 0, -1];
let offset_y: [isize; 8] = [0, -1, -1, -1, 0, 1, 1, 1];
let mut neighbours = Vec::new();
for i in 0..8 {
let nx = self.position.x() offset_x[i];
let ny = self.position.y() offset_y[i];
if Node::is_valid((nx, ny), maze) {
let (nx, ny) = (nx as usize, ny as usize);
let mut node = Node::new(Position((nx, ny)), self, maze.end().unwrap());
node.previous = Some(self as *const _);
neighbours.push(node);
} else {
continue;
}
}
neighbours
}
請記住,當我們將指向它的原始指標()分配給我們新創建的節點時,該previous節點位于堆疊上。*const Node(另外附注,該Previous節點最初是從 a 上彈出PriorityQueue并移動到變數中的stack)
以前的:
stack
之后,他們被移動到的那個向量中的(節點)PriorityQueue我們將呼叫這個佇列open(或者只是被丟棄,但這并不重要)。
節點:
Vec->open: PriorityQueue
到目前為止一切順利,因為Previous節點尚未移動,原始指標仍應指向它。
但是在這些節點進入之后PriorityQueue,Previous節點也被移動到另一個PriorityQueue讓我們呼叫該佇列closed。
上一個:
stack->closed: PriorityQueue
現在我的問題最后是,指向先前節點的原始指標會發生什么,這是未定義的行為,是否意味著 now 指標不再指向該分配?
TLDR:
當非 Copy 值被移動時記憶體如何作業(它仍然在同一個記憶體地址上)?只有值的所有權發生變化并且值保持在相同的記憶體地址上,還是它改變了記憶體地址,獲得新的記憶體分配并被復制到那里?如果這可能有點過于籠統,那么我的Node結構會發生什么,那么當它被移動時,那些指向被移動的指標previous Node會無效嗎?
uj5u.com熱心網友回復:
當一個節點被移動時,它實際上存盤在其他地方,因此它的地址是不一樣的(通常,即使一些優化可以防止這種情況發生,但沒有任何保證)。
下面的最小示例顯示了這一點;初始節點在堆疊上創建,然后我們將它們移動到向量的堆分配存盤中。顯示所有這些節點的地址表明這些地址確實不相同。
的previous成員n1,用它在堆疊上時的地址初始化,n0一旦n0移動到向量,就變成一個懸空指標。的位置n0可能已被其他一些變數重用,而通過指標訪問此記憶體位置時發生的情況實際上是 undefined。
這就是為什么取消參考原始指標是unsafeRust 中的一項操作(這也可能是 Rust 存在的原因)。
獨立于Copy特征,復制值和移動值之間的差異是微妙的。在這種情況下,Node它僅由基本型別(整數和原始指標,最終為 null if None)組成,并且不使用此指標執行記憶體管理(Vec與它包含的指標一樣)。因此,移動 aNode恰好在物理上與復制它是一樣的:或多或少是結構成員的按位復制,就像 isNode一樣Copy。
例如,當涉及到Vec我們想要移動的 a 時,移動操作會更有趣。(它被移動到的位置)的新位置Vec將指標(按位復制)保留到堆分配存盤中,如在初始位置(它被移動到的位置)中已知的那樣。然后,這個移動位置不再被認為是這個堆分配存盤的所有者:我們已經在移動位置和移動到位置之間轉移了堆分配存盤的所有權,但我們不必復制這個堆分配的存盤。這是復制/克隆上的移動操作的主要興趣(通過簡單地轉移所有權來保存堆分配存盤的重復)。這種情況在下面的示例中可見,當nodes移至nodes_again:Vec本身被移動,但Node它包含的 s 沒有。
? The Rust Programming Language ? 的這一部分很好地解釋了這種情況,使用 aString而不是 a Vec。
#[derive(Debug)]
struct Node {
value: i32,
previous: Option<*const Node>,
}
fn main() {
let n0 = Node {
value: 0,
previous: None,
};
let n1 = Node {
value: 1,
previous: Some(&n0 as *const _),
};
println!("n0 at {:?}: {:?}", &n0 as *const _, n0);
println!("n1 at {:?}: {:?}", &n1 as *const _, n1);
println!("~~~~~~~~");
let nodes = vec![n0, n1];
println!("nodes at {:?}", &nodes as *const _);
for (i, n) in nodes.iter().enumerate() {
println!("nodes[{}] at {:?}: {:?}", i, n as *const _, n);
}
println!("~~~~~~~~");
let nodes_again = nodes;
println!("nodes_again at {:?}", &nodes_again as *const _);
for (i, n) in nodes_again.iter().enumerate() {
println!("nodes_again[{}] at {:?}: {:?}", i, n as *const _, n);
}
}
/*
n0 at 0x7ffd2dd14520: Node { value: 0, previous: None }
n1 at 0x7ffd2dd14500: Node { value: 1, previous: Some(0x7ffd2dd14520) }
~~~~~~~~
nodes at 0x7ffd2dd144e8
nodes[0] at 0x558bad50fba0: Node { value: 0, previous: None }
nodes[1] at 0x558bad50fbb8: Node { value: 1, previous: Some(0x7ffd2dd14520) }
~~~~~~~~
nodes_again at 0x7ffd2dd14490
nodes_again[0] at 0x558bad50fba0: Node { value: 0, previous: None }
nodes_again[1] at 0x558bad50fbb8: Node { value: 1, previous: Some(0x7ffd2dd14520) }
*/
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/521917.html
標籤:指针记忆锈堆内存堆栈内存
