我可以在捕获环境的同时实现特征吗?
Can I implement a trait while capturing the environment?
我正在尝试对 Advent of Code 2019 实施 A* 搜索(是的,我知道 Slowpoke)。我是这样开始的:
fn find_path(start: Coords, goal: Coords, map: &Vec<Vec<char>>) -> Vec<Coords> {
struct Node {
distance: u32,
pos: Coords,
}
impl PartialEq for Node {
fn eq(&self, other: &Self) -> bool {
self.distance + manhattan(self.pos, goal) == other.distance + manhattan(other.pos, goal)
}
}
...
let mut edge = BinaryHeap::new();
edge.push(Node{distance: 0, pos: start});
...
Coords
是一个带有 x
和 y
的结构。这里的问题是我不能在特征中使用 goal
,因为它不在范围内。闭包可以捕获它,但我怀疑我是否可以在这里使用闭包而不是 fn
。如果是这样,语法是什么?如果没有,是否有无法完成的根本原因?我无法在网上找到答案。
我知道简单的解决方案是将 goal
包含在 Node
中,但这是多余的,因为我将在 A* 期间创建数千个 Node
,所有这些都会有相同的目标,浪费内存和 CPU 周期。原则上,goal
可以是单个全局变量,但这是一个不整洁的选项。
尽管我确信在 Node
中包含 goal
在实践中会很好,但我宁愿不这样做。
是否有另一种惯用的方法来完成我想做的事情?
不,您不能在 impl
块中捕获任何环境。闭包捕获环境,因此您不能将闭包用作 impl
块中的函数。
函数和方法被设计为从任何上下文中调用,因此不能保证甚至有一个环境可以被捕获。事实上,我们可以在另一个函数内部声明类型、函数、方法等,这基本上是一种语法上的好处。
我可能会创建一个包装 Node
和 goal
的类型:
struct Foo(Node, Coord);
impl Foo {
fn value(&self) -> WhateverType {
self.0.distance + manhattan(self.0.pos, self.1)
}
}
impl PartialEq for Foo {
fn eq(&self, other: &Self) -> bool {
self.value() == other.value()
}
}
另请参阅:
我需要 PartialEq
的原因是 Node
随后将进入 BinaryHeap
。如果 BinaryHeap
可以选择提供自定义比较器,那将是完美的:我不需要 Node
成为 Ord
,我可以让 goal
驻留在那个比较器(闭包)。
似乎正在考虑中:https://github.com/rust-lang/rust/pull/69454
与此同时,有一个提供该功能的板条箱:https://crates.io/crates/binary-heap-plus
目前,我将接受 Node
内 goal
的开销,但了解我的选择是件好事。
我正在尝试对 Advent of Code 2019 实施 A* 搜索(是的,我知道 Slowpoke)。我是这样开始的:
fn find_path(start: Coords, goal: Coords, map: &Vec<Vec<char>>) -> Vec<Coords> {
struct Node {
distance: u32,
pos: Coords,
}
impl PartialEq for Node {
fn eq(&self, other: &Self) -> bool {
self.distance + manhattan(self.pos, goal) == other.distance + manhattan(other.pos, goal)
}
}
...
let mut edge = BinaryHeap::new();
edge.push(Node{distance: 0, pos: start});
...
Coords
是一个带有 x
和 y
的结构。这里的问题是我不能在特征中使用 goal
,因为它不在范围内。闭包可以捕获它,但我怀疑我是否可以在这里使用闭包而不是 fn
。如果是这样,语法是什么?如果没有,是否有无法完成的根本原因?我无法在网上找到答案。
我知道简单的解决方案是将 goal
包含在 Node
中,但这是多余的,因为我将在 A* 期间创建数千个 Node
,所有这些都会有相同的目标,浪费内存和 CPU 周期。原则上,goal
可以是单个全局变量,但这是一个不整洁的选项。
尽管我确信在 Node
中包含 goal
在实践中会很好,但我宁愿不这样做。
是否有另一种惯用的方法来完成我想做的事情?
不,您不能在 impl
块中捕获任何环境。闭包捕获环境,因此您不能将闭包用作 impl
块中的函数。
函数和方法被设计为从任何上下文中调用,因此不能保证甚至有一个环境可以被捕获。事实上,我们可以在另一个函数内部声明类型、函数、方法等,这基本上是一种语法上的好处。
我可能会创建一个包装 Node
和 goal
的类型:
struct Foo(Node, Coord);
impl Foo {
fn value(&self) -> WhateverType {
self.0.distance + manhattan(self.0.pos, self.1)
}
}
impl PartialEq for Foo {
fn eq(&self, other: &Self) -> bool {
self.value() == other.value()
}
}
另请参阅:
我需要 PartialEq
的原因是 Node
随后将进入 BinaryHeap
。如果 BinaryHeap
可以选择提供自定义比较器,那将是完美的:我不需要 Node
成为 Ord
,我可以让 goal
驻留在那个比较器(闭包)。
似乎正在考虑中:https://github.com/rust-lang/rust/pull/69454
与此同时,有一个提供该功能的板条箱:https://crates.io/crates/binary-heap-plus
目前,我将接受 Node
内 goal
的开销,但了解我的选择是件好事。