包含一条数据和被引用数据的结构体中的生命周期
Lifetimes in Structs that Contain both a piece of data and the data being referenced
我正在编写一个小的 Cards Against Humanity 克隆供个人使用,并将其作为学习 Rust 的机会。我目前有以下结构:
// game.rs
pub struct Game<'c> {
deck: Deck,
players: Vec<Player<'c>>,
current_czar_index: usize,
}
// player.rs
pub struct Player<'c> {
pub hand: Vec<&'c Card>,
pub max_hand_size: usize,
pub is_czar: bool,
pub score: u8,
pub name: String,
}
// deck.rs
struct DiscardPile {
pub white: Mutex<Vec<usize>>,
pub black: Mutex<Vec<usize>>,
}
pub struct Deck {
white: Vec<Card>,
black: Vec<Card>,
pub used_sets: Vec<String>,
discard_pile: DiscardPile,
}
// card.rs
pub struct Card {
pub text: String,
pub uuid: Uuid,
pub pick: Option<u8>,
}
还有其他一些(即用于读取 JSON 文件的集合),但它们不相关。
我在game.rs
中有一个函数
pub fn deal_hands(&self) -> Result<(), Box<dyn std::error::Error>> {
for player in &mut self.players {
while player.hand.len() < player.max_hand_size {
player.add_card(self.deck.draw_white().unwrap())?;
}
}
Ok(())
}
出现以下错误:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/game.rs:42:31
|
42 | player.add_card(self.deck.draw_white().unwrap())?;
| ^^^^^^^^^^
|
它明确表示它期待 &mut Player<'_>
但发现生命周期为 &mut Player<'c>
。
HERE 如果您想直接查看代码。
如何修复此类生命周期错误,或者我是否需要重新考虑我的架构?
我觉得这个架构不错。您可能只是在某处错过了生命周期参数的传递。
我不完全确定,但我认为问题可能是 .draw_white()
正在 returning Option<&Card>
并且不清楚所包含的 Card
的生命周期是多少] 应该。我相信这种情况是,任何时候你 return 一个借来的价值,你都必须为借来的价值附加一个生命周期。否则,借用检查器无法判断借入值的有效期。
也许可以试试下面的 .draw_white()
定义
pub fn draw_white<'a>(&self) -> Option<&'a Card> {
然后您需要将 deal_hands()
调整为如下所示。 (正在找出正确的语法,但我认为它是这样的。)
pub fn deal_hands(&self) -> Result<(), Box<dyn std::error::Error>> {
for player<'c> in &mut self.players {
while player.hand.len() < player.max_hand_size {
player.add_card(self.deck.draw_white<'c>().unwrap())?;
}
}
Ok(())
}
我想我已经解决了。幸运的是,在克服了一些错误之后,一切都构建得很好。
原始错误
好的。这是我在尝试构建您的箱子时看到的完整错误。
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/game.rs:42:31
|
42 | player.add_card(self.deck.draw_white().unwrap())?;
| ^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 39:2...
--> src/game.rs:39:2
|
39 | pub fn deal_hands(&self) -> Result<(), Box<dyn std::error::Error>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src/game.rs:42:21
|
42 | player.add_card(self.deck.draw_white().unwrap())?;
| ^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'c` as defined on the impl at 20:6...
--> src/game.rs:20:6
|
20 | impl<'c> Game<'c> {
| ^^
note: ...so that the types are compatible
--> src/game.rs:42:12
|
42 | player.add_card(self.deck.draw_white().unwrap())?;
| ^^^^^^^^
= note: expected `&mut Player<'_>`
found `&mut Player<'c>`
看起来问题实际上出在 deal_hands()
上。您需要将生命周期指定为 &'c mut self
,而不是让 &self
的生命周期为 elided。 (mut
是因为你正在变异 self.players
。)我不完全确定为什么 Rust 没有将 self
的生命周期省略为 'c
,但这可能是另一个原因主题。
下一个错误
我 运行 遇到的下一个错误是
error: captured variable cannot escape `FnMut` closure body
--> src/game.rs:106:17
|
106 | .map(|num| player.play_card_by_index(num as usize))
| - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a reference to a
captured variable which escapes the closure body
| |
| inferred to be a `FnMut` closure
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
对于这一个,问题实际上不在错误消息中。您需要在 .play_card_by_index()
的 return 值中指定 Card
的生命周期,因为否则不清楚对 Card
的引用实际上是否超过闭包。这意味着您需要将 .play_card_by_index()
调整为 return &'c Card' instead of
&Card`。
最终错误
您的 .get_player_names()
中出现了一个小错误。您只需要使用 .iter()
而不是 .into_iter()
,因为前者不会取得 self
的所有权。另外,出于类似的原因,您可能也想 .clone()
播放器名称。
完全差异
这是完整的差异
diff --git a/src/game.rs b/src/game.rs
index 573d09d..5ccbf6b 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -36,7 +36,7 @@ impl<'c> Game<'c> {
/// Hands need to be filled on every turn, so this lets us fill everyones'
/// hands at once.
- pub fn deal_hands(&self) -> Result<(), Box<dyn std::error::Error>> {
+ pub fn deal_hands(&'c mut self) -> Result<(), Box<dyn std::error::Error>> {
for player in &mut self.players {
while player.hand.len() < player.max_hand_size {
player.add_card(self.deck.draw_white().unwrap())?;
@@ -124,7 +124,10 @@ impl<'c> Game<'c> {
}
pub fn get_player_names(&self) -> Vec<String> {
- self.players.into_iter().map(|player| player.name).collect()
+ self.players
+ .iter()
+ .map(|player| player.name.clone())
+ .collect()
}
}
diff --git a/src/player.rs b/src/player.rs
index 4bd6848..9f95373 100644
--- a/src/player.rs
+++ b/src/player.rs
@@ -46,7 +46,7 @@ impl<'c> Player<'c> {
/// Used in the TUI, we can play a card by index and get the reference
/// to the card we played
- pub fn play_card_by_index(&mut self, index: usize) -> & Card {
+ pub fn play_card_by_index(&mut self, index: usize) -> &'c Card {
self.hand.remove(index)
}
@@ -56,7 +56,7 @@ impl<'c> Player<'c> {
/// instead of making the card on the client and sending the full card info,
/// we can just send the UUID[s] of the cards that were played with some
/// data about the order they were played in.
- pub fn play_card_by_uuid(&mut self, uuid: Uuid) -> Option<& Card> {
+ pub fn play_card_by_uuid(&mut self, uuid: Uuid) -> Option<&Card> {
// TODO: Find a better way to do this
let mut index_of_card: usize = self.hand.len() + 1;
for (index, card) in self.hand.iter().enumerate() {
祝你好运!
这看起来是个有趣的项目。我希望你继续用 Rust 构建它!
我正在编写一个小的 Cards Against Humanity 克隆供个人使用,并将其作为学习 Rust 的机会。我目前有以下结构:
// game.rs
pub struct Game<'c> {
deck: Deck,
players: Vec<Player<'c>>,
current_czar_index: usize,
}
// player.rs
pub struct Player<'c> {
pub hand: Vec<&'c Card>,
pub max_hand_size: usize,
pub is_czar: bool,
pub score: u8,
pub name: String,
}
// deck.rs
struct DiscardPile {
pub white: Mutex<Vec<usize>>,
pub black: Mutex<Vec<usize>>,
}
pub struct Deck {
white: Vec<Card>,
black: Vec<Card>,
pub used_sets: Vec<String>,
discard_pile: DiscardPile,
}
// card.rs
pub struct Card {
pub text: String,
pub uuid: Uuid,
pub pick: Option<u8>,
}
还有其他一些(即用于读取 JSON 文件的集合),但它们不相关。
我在game.rs
pub fn deal_hands(&self) -> Result<(), Box<dyn std::error::Error>> {
for player in &mut self.players {
while player.hand.len() < player.max_hand_size {
player.add_card(self.deck.draw_white().unwrap())?;
}
}
Ok(())
}
出现以下错误:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/game.rs:42:31
|
42 | player.add_card(self.deck.draw_white().unwrap())?;
| ^^^^^^^^^^
|
它明确表示它期待 &mut Player<'_>
但发现生命周期为 &mut Player<'c>
。
HERE 如果您想直接查看代码。
如何修复此类生命周期错误,或者我是否需要重新考虑我的架构?
我觉得这个架构不错。您可能只是在某处错过了生命周期参数的传递。
我不完全确定,但我认为问题可能是 .draw_white()
正在 returning Option<&Card>
并且不清楚所包含的 Card
的生命周期是多少] 应该。我相信这种情况是,任何时候你 return 一个借来的价值,你都必须为借来的价值附加一个生命周期。否则,借用检查器无法判断借入值的有效期。
也许可以试试下面的 .draw_white()
定义
pub fn draw_white<'a>(&self) -> Option<&'a Card> {
然后您需要将 deal_hands()
调整为如下所示。 (正在找出正确的语法,但我认为它是这样的。)
pub fn deal_hands(&self) -> Result<(), Box<dyn std::error::Error>> {
for player<'c> in &mut self.players {
while player.hand.len() < player.max_hand_size {
player.add_card(self.deck.draw_white<'c>().unwrap())?;
}
}
Ok(())
}
我想我已经解决了。幸运的是,在克服了一些错误之后,一切都构建得很好。
原始错误
好的。这是我在尝试构建您的箱子时看到的完整错误。
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/game.rs:42:31
|
42 | player.add_card(self.deck.draw_white().unwrap())?;
| ^^^^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 39:2...
--> src/game.rs:39:2
|
39 | pub fn deal_hands(&self) -> Result<(), Box<dyn std::error::Error>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src/game.rs:42:21
|
42 | player.add_card(self.deck.draw_white().unwrap())?;
| ^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'c` as defined on the impl at 20:6...
--> src/game.rs:20:6
|
20 | impl<'c> Game<'c> {
| ^^
note: ...so that the types are compatible
--> src/game.rs:42:12
|
42 | player.add_card(self.deck.draw_white().unwrap())?;
| ^^^^^^^^
= note: expected `&mut Player<'_>`
found `&mut Player<'c>`
看起来问题实际上出在 deal_hands()
上。您需要将生命周期指定为 &'c mut self
,而不是让 &self
的生命周期为 elided。 (mut
是因为你正在变异 self.players
。)我不完全确定为什么 Rust 没有将 self
的生命周期省略为 'c
,但这可能是另一个原因主题。
下一个错误
我 运行 遇到的下一个错误是
error: captured variable cannot escape `FnMut` closure body
--> src/game.rs:106:17
|
106 | .map(|num| player.play_card_by_index(num as usize))
| - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returns a reference to a
captured variable which escapes the closure body
| |
| inferred to be a `FnMut` closure
|
= note: `FnMut` closures only have access to their captured variables while they are executing...
= note: ...therefore, they cannot allow references to captured variables to escape
对于这一个,问题实际上不在错误消息中。您需要在 .play_card_by_index()
的 return 值中指定 Card
的生命周期,因为否则不清楚对 Card
的引用实际上是否超过闭包。这意味着您需要将 .play_card_by_index()
调整为 return &'c Card' instead of
&Card`。
最终错误
您的 .get_player_names()
中出现了一个小错误。您只需要使用 .iter()
而不是 .into_iter()
,因为前者不会取得 self
的所有权。另外,出于类似的原因,您可能也想 .clone()
播放器名称。
完全差异
这是完整的差异
diff --git a/src/game.rs b/src/game.rs
index 573d09d..5ccbf6b 100644
--- a/src/game.rs
+++ b/src/game.rs
@@ -36,7 +36,7 @@ impl<'c> Game<'c> {
/// Hands need to be filled on every turn, so this lets us fill everyones'
/// hands at once.
- pub fn deal_hands(&self) -> Result<(), Box<dyn std::error::Error>> {
+ pub fn deal_hands(&'c mut self) -> Result<(), Box<dyn std::error::Error>> {
for player in &mut self.players {
while player.hand.len() < player.max_hand_size {
player.add_card(self.deck.draw_white().unwrap())?;
@@ -124,7 +124,10 @@ impl<'c> Game<'c> {
}
pub fn get_player_names(&self) -> Vec<String> {
- self.players.into_iter().map(|player| player.name).collect()
+ self.players
+ .iter()
+ .map(|player| player.name.clone())
+ .collect()
}
}
diff --git a/src/player.rs b/src/player.rs
index 4bd6848..9f95373 100644
--- a/src/player.rs
+++ b/src/player.rs
@@ -46,7 +46,7 @@ impl<'c> Player<'c> {
/// Used in the TUI, we can play a card by index and get the reference
/// to the card we played
- pub fn play_card_by_index(&mut self, index: usize) -> & Card {
+ pub fn play_card_by_index(&mut self, index: usize) -> &'c Card {
self.hand.remove(index)
}
@@ -56,7 +56,7 @@ impl<'c> Player<'c> {
/// instead of making the card on the client and sending the full card info,
/// we can just send the UUID[s] of the cards that were played with some
/// data about the order they were played in.
- pub fn play_card_by_uuid(&mut self, uuid: Uuid) -> Option<& Card> {
+ pub fn play_card_by_uuid(&mut self, uuid: Uuid) -> Option<&Card> {
// TODO: Find a better way to do this
let mut index_of_card: usize = self.hand.len() + 1;
for (index, card) in self.hand.iter().enumerate() {
祝你好运!
这看起来是个有趣的项目。我希望你继续用 Rust 构建它!