Rust 中 self 和 collections 的可变性问题
Mutability issue with self and collections in Rust
我正在用 Rust 编写 simple library 来管理一副纸牌。具有洗牌、发牌等功能
shuffle() 函数采用对 self 的可变引用,允许对现有牌组进行重新洗牌。它应该非常简单:
- 从包含元组的牌组中创建一个临时 collection,其中包含一张牌和一个随机数。
- 按随机数对 collection 进行排序。
- 使用临时 collection 的顺序重建现有甲板。
代码如下。
pub struct Deck {
// A deck contains zero or more cards
cards: Vec<Card>
}
impl Deck {
// shuffle
pub fn shuffle(&mut self) {
if self.cards.is_empty() {
return;
}
let mut shuffler : Vec<(&Card, u32)> = Vec::with_capacity(self.cards.len());
for card in self.cards.iter() {
// make a tuple consisting of each card in the input and a random number
let card_pos = (card, rand::thread_rng().gen::<u32>());
shuffler.push(card_pos);
}
// Sort the vector
shuffler.sort_by_key(|k| k.1);
// Clear the cards
self.cards.clear();
// Put the cards into the new randomized order
for card_pos in shuffler {
let (card, _) = card_pos;
self.cards.push(*card)
}
}
}
我遇到的问题是无法编译,因为出现错误。
src\deck.rs:85:9: 85:19 error: cannot borrow `self.cards` as mutable because it is also borrowed as immutable [E0502]
src\deck.rs:85 self.cards.clear();
^~~~~~~~~~
src\deck.rs:75:15: 75:25 note: previous borrow of `self.cards` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `self.cards` until the borrow ends
src\deck.rs:75 for card in self.cards.iter() {
^~~~~~~~~~
src\deck.rs:92:6: 92:6 note: previous borrow ends here
src\deck.rs:68 pub fn shuffle(&mut self) {
...
src\deck.rs:92 }
^
src\deck.rs:90:13: 90:23 error: cannot borrow `self.cards` as mutable because it is also borrowed as immutable [E0502]
src\deck.rs:90 self.cards.push(*card)
^~~~~~~~~~
src\deck.rs:75:15: 75:25 note: previous borrow of `self.cards` occurs here; the immutable borrow prevent
s subsequent moves or mutable borrows of `self.cards` until the borrow ends
src\deck.rs:75 for card in self.cards.iter() {
^~~~~~~~~~
src\deck.rs:92:6: 92:6 note: previous borrow ends here
src\deck.rs:68 pub fn shuffle(&mut self) {
...
src\deck.rs:92 }
错误抱怨可变性,我认为这意味着它不喜欢我在范围内有多个可变引用或其他东西,但我不知道如何修复它。我试过使用大括号定界符为每个动作制作块,但无济于事。我可能会将其分解为多个函数,但我宁愿它是一个函数。我如何以最少的努力完成这项工作?
注意,我还没有测试排序功能,所以我希望 sort_by_key 能按照我的想法行事,但这只有在我解决了第一个问题后才有意义。
shuffler
是 Vec<(&Card, u32)>
类型,即 card
是 reference。也就是说,它是 Card
对象的指针,该对象存储在 self.cards
向量下方的缓冲区中。所以self.cards.clear()
会删除shuffler
脚下的记忆!
幸运的是,有一个简单的解决方法:不要引用并清除矢量,将卡片移出 self.cards
drain
:
let mut shuffler: Vec<(Card, u32)> = Vec::with_capacity(self.cards.len());
for card in self.cards.drain(..) {
let card_pos = (card, rand::thread_rng().gen::<u32>());
shuffler.push(card_pos);
}
shuffler.sort_by_key(|k| k.1);
for card_pos in shuffler {
let (card, _) = card_pos;
self.cards.push(card);
}
旁白:有一种就地改组算法也比排序更有效——线性时间而不是 O(n log n),以及更好的常数因子——Fisher-Yates shuffle。
我正在用 Rust 编写 simple library 来管理一副纸牌。具有洗牌、发牌等功能
shuffle() 函数采用对 self 的可变引用,允许对现有牌组进行重新洗牌。它应该非常简单:
- 从包含元组的牌组中创建一个临时 collection,其中包含一张牌和一个随机数。
- 按随机数对 collection 进行排序。
- 使用临时 collection 的顺序重建现有甲板。
代码如下。
pub struct Deck {
// A deck contains zero or more cards
cards: Vec<Card>
}
impl Deck {
// shuffle
pub fn shuffle(&mut self) {
if self.cards.is_empty() {
return;
}
let mut shuffler : Vec<(&Card, u32)> = Vec::with_capacity(self.cards.len());
for card in self.cards.iter() {
// make a tuple consisting of each card in the input and a random number
let card_pos = (card, rand::thread_rng().gen::<u32>());
shuffler.push(card_pos);
}
// Sort the vector
shuffler.sort_by_key(|k| k.1);
// Clear the cards
self.cards.clear();
// Put the cards into the new randomized order
for card_pos in shuffler {
let (card, _) = card_pos;
self.cards.push(*card)
}
}
}
我遇到的问题是无法编译,因为出现错误。
src\deck.rs:85:9: 85:19 error: cannot borrow `self.cards` as mutable because it is also borrowed as immutable [E0502]
src\deck.rs:85 self.cards.clear();
^~~~~~~~~~
src\deck.rs:75:15: 75:25 note: previous borrow of `self.cards` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `self.cards` until the borrow ends
src\deck.rs:75 for card in self.cards.iter() {
^~~~~~~~~~
src\deck.rs:92:6: 92:6 note: previous borrow ends here
src\deck.rs:68 pub fn shuffle(&mut self) {
...
src\deck.rs:92 }
^
src\deck.rs:90:13: 90:23 error: cannot borrow `self.cards` as mutable because it is also borrowed as immutable [E0502]
src\deck.rs:90 self.cards.push(*card)
^~~~~~~~~~
src\deck.rs:75:15: 75:25 note: previous borrow of `self.cards` occurs here; the immutable borrow prevent
s subsequent moves or mutable borrows of `self.cards` until the borrow ends
src\deck.rs:75 for card in self.cards.iter() {
^~~~~~~~~~
src\deck.rs:92:6: 92:6 note: previous borrow ends here
src\deck.rs:68 pub fn shuffle(&mut self) {
...
src\deck.rs:92 }
错误抱怨可变性,我认为这意味着它不喜欢我在范围内有多个可变引用或其他东西,但我不知道如何修复它。我试过使用大括号定界符为每个动作制作块,但无济于事。我可能会将其分解为多个函数,但我宁愿它是一个函数。我如何以最少的努力完成这项工作?
注意,我还没有测试排序功能,所以我希望 sort_by_key 能按照我的想法行事,但这只有在我解决了第一个问题后才有意义。
shuffler
是 Vec<(&Card, u32)>
类型,即 card
是 reference。也就是说,它是 Card
对象的指针,该对象存储在 self.cards
向量下方的缓冲区中。所以self.cards.clear()
会删除shuffler
脚下的记忆!
幸运的是,有一个简单的解决方法:不要引用并清除矢量,将卡片移出 self.cards
drain
:
let mut shuffler: Vec<(Card, u32)> = Vec::with_capacity(self.cards.len());
for card in self.cards.drain(..) {
let card_pos = (card, rand::thread_rng().gen::<u32>());
shuffler.push(card_pos);
}
shuffler.sort_by_key(|k| k.1);
for card_pos in shuffler {
let (card, _) = card_pos;
self.cards.push(card);
}
旁白:有一种就地改组算法也比排序更有效——线性时间而不是 O(n log n),以及更好的常数因子——Fisher-Yates shuffle。