我可以在捕获环境的同时实现特征吗?

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 是一个带有 xy 的结构。这里的问题是我不能在特征中使用 goal,因为它不在范围内。闭包可以捕获它,但我怀疑我是否可以在这里使用闭包而不是 fn。如果是这样,语法是什么?如果没有,是否有无法完成的根本原因?我无法在网上找到答案。

我知道简单的解决方案是将 goal 包含在 Node 中,但这是多余的,因为我将在 A* 期间创建数千个 Node,所有这些都会有相同的目标,浪费内存和 CPU 周期。原则上,goal 可以是单个全局变量,但这是一个不整洁的选项。

尽管我确信在 Node 中包含 goal 在实践中会很好,但我宁愿不这样做。

是否有另一种惯用的方法来完成我想做的事情?

不,您不能在 impl 块中捕获任何环境。闭包捕获环境,因此您不能将闭包用作 impl 块中的函数。

函数和方法被设计为从任何上下文中调用,因此不能保证甚至有一个环境可以被捕获。事实上,我们可以在另一个函数内部声明类型、函数、方法等,这基本上是一种语法上的好处。

我可能会创建一个包装 Nodegoal 的类型:

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

目前,我将接受 Nodegoal 的开销,但了解我的选择是件好事。