具有生命周期和多个引用的 Rust 程序的糟糕设计

问题描述 投票:0回答:1

我对 Rust 语言非常陌生,所以请耐心等待。

我正在用 Rust 实现一个图形数据结构。对于图中的每个节点(以及边 - 从这个片段中省略),我有一个结构“算法”(可能命名不好),它执行一些基于节点和边的逻辑。算法需要了解图的连通性,因此本身不能属于节点或边。它们基于特征多态性,尽管我计划稍后将其重构为枚举多态性。最后,我有一个求解器,它通过迭代图的节点并为其创建算法来工作。请注意,该示例经过大量精炼。

但是,我有两个问题。第一个是由于“算法”向量中 Boxed 结构的生命周期。

error: lifetime may not live long enough
  --> main.rs:67:29
   |
51 |   impl<'a> Solver<'a> {
   |        -- lifetime `'a` defined here
...
63 |       pub fn solve(&mut self) {
   |                    - let's call the lifetime of this reference `'1`
...
67 |               let algorithm = Box::new(MyAlgorithm {
   |  _____________________________^
68 | |                 graph: &mut self.graph,
69 | |             });
   | |______________^ assignment requires that `'1` must outlive `'a`

我已尽力向编译器指示 Algorithm 结构体的生命周期应保持在 Solver 结构体的生命周期内,但这并没有奏效。

完成其工作后,算法将需要更新图的节点和边,因此它需要对图对象的可变引用。然而,由于第二个可变借用,这当然是不正确的。

cannot borrow `self.graph` as mutable more than once at a time
second mutable borrow occurs here

下面是代码。

struct Node {
    // Define the Node struct...
}

impl Node {
    // Implement methods for Node...
}

struct Graph {
    nodes: Vec<Node>,
}

impl Graph {
    fn new() -> Self {
        Graph {
            nodes: Vec::new(),
            // Initialize other fields...
        }
    }
}

trait BaseAlgorithm<'a> {
    // Define the trait methods...
}

// Create a MyAlgorithm struct that takes a mutable reference to the graph
struct MyAlgorithm<'a> {
    graph: &'a mut Graph,
}

impl<'a> BaseAlgorithm<'a> for MyAlgorithm<'a> {
    // Implement the trait methods for MyAlgorithm...
}

struct Solver<'a> {
    algorithms: Vec<Box<dyn BaseAlgorithm<'a> + 'a>>,
    graph: Graph,
}

impl<'a> Solver<'a> {
    fn new() -> Self {
        Solver {
            algorithms: Vec::new(),
            graph: Graph::new(),
        }
    }

    fn add_algorithm(&mut self, algorithm: Box<dyn BaseAlgorithm<'a> + 'a>) {
        self.algorithms.push(algorithm);
    }

    pub fn solve(&mut self) {
        // Loop over the nodes vector within Graph
        for node in &mut self.graph.nodes {
            // add an algorithm with a mutable reference to Graph
            let algorithm = Box::new(MyAlgorithm {
                graph: &mut self.graph,
            });
            self.add_algorithm(algorithm);
        }
    }
}

fn main() {
    
    let mut solver = Solver::new();

    solver.solve();

}

如果可以修复设计,那么我将非常感谢任何帮助实现这一目标。但是,如果设计无法修复,是否有惯用的 Rust 模式来实现此逻辑的要求?

rust reference borrow-checker mutable
1个回答
0
投票

这个问题的答案取决于这个问题:算法什么时候需要修改图?如果求解器一次仅使用一种算法,则可以将图作为参数传递给求解器在算法上调用的函数,而不是始终存储对其的可变引用。

如果算法确实需要在所有时间对图进行可变访问,那么

&mut
无法实现这一点。您将需要使用内部可变性以不同的方式提供可变性。如果程序是单线程的,则可以使用
RefCell
;如果是多线程的,则可以使用
Mutex
RwLock
。因此,您可以将图表存储为
RefCell<Graph>
。这使得即使您只有对
RefCell
的不可变引用,也可以可变地借用该图。然后,简单地将引用单元借用到每个算法(如
Algorithm{ graph: &self.graph }
)中可能会起作用,但如果没有,您可以通过引入引用计数来使其起作用。这使您不必担心生命周期,因为对数据的所有引用都会被计数,并且当没有剩余引用时它将被删除。单线程的引用计数指针类型为
Rc
,多线程的引用计数指针类型为
Arc
。因此,您可以根据多线程情况将图表存储为
Rc<RefCell<Graph>>
Arc<RwLock<Graph>>
。现在,使用
Rc
Arc
,您可以调用
clone()
来获取第二个
Rc
Arc
指向与第一个相同的数据。如果该数据是内部可变的,例如
RefCell
Mutex
RwLock
,那么您可以可变地借用其内容。

© www.soinside.com 2019 - 2024. All rights reserved.