Rust 中自引用结构的替代方案?

Pet*_*ter 8 rust

这里有很多关于 Rust 中的自引用结构的问题,我想我已经阅读了所有这些问题,但我仍然无法理解这些问题。在 Rust 中处理自引用结构有哪些设计模式?我会列出我的想法,以便其他人可以告诉我哪里出错了(我有一种感觉,它是在开始时)。

当我学习一门新语言时,我尝试实现相同的游戏,而对于 Rust,我遇到了这个问题:我们有资源,其中一些可以从其他资源中制作。(假设它们形成一个 DAG。)

我对它进行建模的天真尝试是这样的:

struct Resource {
  name: String,
  production_cost: HashMap<&Resource, i32>,
  // other data
}
Run Code Online (Sandbox Code Playgroud)

我们需要一个引用的生命周期注解,所以它变成Resource<'a>了一个HashMap<&'a Resource, i32>.

Vec<Resource>这种形式的A是不切实际的(不可能?)构建,所以另一个尝试是上升一个级别:

struct Resource {
  name: String,
  // other data
}

struct ResourceConfig {
  resources: Vec<Resource>,
  resource_costs: HashMap<&Resource, HashMap<&Resource, i32>>,
}
Run Code Online (Sandbox Code Playgroud)

我也想不出构造其中一个的方法。我能想到的接下来的两个选项是:

  1. 将所有内容都包裹在 RefCell/Rc(或 Arc/Mutex)中。这似乎需要太多的输入,更不用说引用计数的性能开销(我确信这对我来说是微不足道的)。
  2. 将索引传递给主向量。

所以最终结果(2)看起来像:

type RIndex = usize;
type ResourceSet = HashMap<RIndex, i32>;

struct Resource {
  name: String,
  // other data
}

struct ResourceConfig {
  resources: Vec<Resource>,
  resource_costs: HashMap<RIndex, ResourceSet>,
}
Run Code Online (Sandbox Code Playgroud)

其余的代码只是传递一堆RIndex“es”(并保留一个&ResourceConfig引用来进行转换)。我可以RIndex以击键为代价从类型别名升级到类型安全的新类型(可能值得吗?),但最后我觉得我只是在做我自己的指针管理——而不是担心无效/空我担心RIndex超出范围。

我在这里缺少什么?(不安全的代码?)

在 C++ 中,我会做类似的事情:

class Resource {
  std::string name;
  std::unordered_map<Resource*, int> production_cost;
  // Probably wrap the unordered_map in its own class, maybe with a Resource reference, etc.
}
Run Code Online (Sandbox Code Playgroud)

(当然,我会失去生命周期保证,但资源都将位于同一对象中的某个地方,因此工作起来并不难。)

Rei*_*iks 4

\n

将索引传递给主向量。

\n
\n

您正在描述一个 arena \xe2\x80\x93 ,一个可以通过索引相互链接的结构向量。有一些不错的包可以让你做到这一点,并提供一些编译时检查。

\n

对于您的情况,我们可以更进一步\xe2\x80\x93,因为您不需要在创建资源类型后销毁该资源类型,所有资源都可以在相同的生命周期内生存(一旦创建) 。因此,理论上您应该能够让竞技场中的项目相互引用。typed-arena可以让你做到这一点!

\n
use typed_arena::Arena;\n\nfn main() {\n    let resources = Arena::<Resource>::default();\n\n    let iron = resources.alloc(Resource {\n        name: "iron".to_string(),\n        cost: vec![],\n    });\n    let water = resources.alloc(Resource {\n        name: "water".to_string(),\n        cost: vec![],\n    });\n    let rust = resources.alloc(Resource {\n        name: "rust".to_string(),\n        cost: vec![iron, water],\n    });\n\n    println!("{iron:?}");\n    println!("{water:?}");\n    println!("{rust:?}");\n}\n\n#[derive(Debug)]\nstruct Resource<\'a> {\n    name: String,\n    cost: Vec<&\'a Resource<\'a>>,\n}\n
Run Code Online (Sandbox Code Playgroud)\n
Resource { name: "iron", cost: [] }\nResource { name: "water", cost: [] }\nResource { name: "rust", cost: [Resource { name: "iron", cost: [] }, Resource { name: "water", cost: [] }] }\n
Run Code Online (Sandbox Code Playgroud)\n