2

我正在编写一个带有边和节点的图形实现。该图应同时访问,因此我选择将边和节点构建为Arc<Mutex<dyn Edge>>Arc<RwLock<dyn Node>>.

不幸的是,在连接节点/边时出现编译错误the parameter type 'T' may not live long enoughPlayground )。

pub trait Node {
  fn connect(&mut self, edge: EdgeRef);
}

pub type NodeRef = Arc<RwLock<dyn Node>>;

pub trait Edge {
  fn connect(&mut self, node: NodeRef);
}

pub type EdgeRef = Arc<Mutex<dyn Edge>>;

impl<T> Node for Arc<RwLock<T>>
where
  T: Node,
{
  fn connect(&mut self, edge_ref: EdgeRef) {
    let mut node = self.write().unwrap();
    let mut edge = edge_ref.lock().unwrap();
    let self_clone = self.clone() as NodeRef; // the parameter type `T` may not live long enough
    edge.connect(self_clone);
    node.connect(edge_ref.clone());
  }
}

问题是:Arc<RwLock<T>>应该不是参考,所以不应该有生命周期。将其转换为Arc<RwLock<dyn Node>>也不会引入生命周期。

有人可以解释这个编译器错误吗?这个问题与每个参数类型(例如Type<T>)有关还是仅与Arc<RwLock<T>>

4

2 回答 2

5

编译错误解释了如何解决问题:

error[E0310]: the parameter type `T` may not live long enough
  --> src/lib.rs:22:22
   |
15 | impl<T> Node for Arc<RwLock<T>>
   |      - help: consider adding an explicit lifetime bound...: `T: 'static`
...
22 |     let self_clone = self.clone() as NodeRef;
   |                      ^^^^^^^^^^^^
   |
note: ...so that the type `T` will meet its required lifetime bounds
  --> src/lib.rs:22:22
   |
22 |     let self_clone = self.clone() as NodeRef;
   |                      ^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0310`.

添加+ 'static到您T的界限确实可以解决错误:

use std::sync::{Arc, Mutex, RwLock};

pub trait Node {
  fn connect(&mut self, edge: EdgeRef);
}

pub type NodeRef = Arc<RwLock<dyn Node>>;

pub trait Edge {
  fn connect(&mut self, node: NodeRef);
}

pub type EdgeRef = Arc<Mutex<dyn Edge>>;

impl<T> Node for Arc<RwLock<T>>
where
  T: Node + 'static, // added "+ 'static" here
{
  fn connect(&mut self, edge_ref: EdgeRef) {
    let mut node = self.write().unwrap();
    let mut edge = edge_ref.lock().unwrap();
    let self_clone = self.clone() as NodeRef;
    edge.connect(self_clone);
    node.connect(edge_ref.clone());
  }
}

操场

但是,当我的 T 永远不会成为参考时,为什么我需要终身绑定呢?你问。好吧,Rust 编译器还不知道,aT可以是任何类型,包括引用。表示的类型T集包括 和 表示的类型&T&mut T&T和都是&mut T的子集T。这就是为什么你必须设置生命周期的原因T,这是你与编译器沟通的方式,你T只会拥有类型或静态引用。

更多关于“静态生命周期”的信息

'static对生命周期来说是一个误导性的名称,因为它使大多数人认为'static类型必须在程序的整个持续时间内都存在,并且不能动态分配或删除。这些都不是真的:'static类型可以动态分配,也可以删除。在实践中'static真正的意思是“你可以安全地无限期地持有这种类型”。所有“拥有的类型”都喜欢String和。这是一个 Rust 程序,我希望能说明这一点:Vec'static

use rand::prelude::*; // 0.7.3

// this function takes 'static types and drops them
// no compiler errors because 'static types can be dynamically allocated and dropped
fn is_static<T: 'static>(t: T) {
    std::mem::drop(t)
}

fn main() {
    let string = String::from("string"); // dynamically allocated string
    is_static(string); // compiles just fine

    let mut strings: Vec<String> = Vec::new();
    let mut loops = 10;
    while loops > 0 {
        if rand::random() {
            strings.push(format!("randomly dynamically allocated string on loop {}", loops));
        }
        loops -= 1;
    }

    // all the strings are 'static
    for string in strings {
        is_static(string); // compiles no problem
    }
}

操场

更多关于生命周期省略和默认特征对象生命周期

你定义NodeRefEdgeRef这样:

pub type NodeRef = Arc<RwLock<dyn Node>>;
pub type EdgeRef = Arc<Mutex<dyn Edge>>;

然而,Rust 编译器是这样解释的:

pub type NodeRef = Arc<RwLock<dyn Node + 'static>>;
pub type EdgeRef = Arc<Mutex<dyn Edge + 'static>>;

所以当你想投一些Arc<RwLock<T>>to NodeRefthenT必须有界,Node + 'static因为NodeRef也有那些界,即Arc<RwLock<dyn Node + 'static>>. Rust 中的所有 trait 对象都有生命周期,但您通常不会编写它们,因为 Rust 会为您推断它们。如果您想了解更多信息,Rust 参考对生命周期省略和默认 trait 对象生命周期有详尽的解释。

'static您可以通过使您的类型别名泛型来缓解要求'a

pub type NodeRef<'a> = Arc<RwLock<dyn Node + 'a>>;
pub type EdgeRef<'a> = Arc<Mutex<dyn Edge + 'a>>;

但是,这将大大增加您的代码的复杂性,我很确定您想坚持下去,'static因为它已经支持您正在尝试做的事情。

于 2020-05-10T13:46:57.783 回答
2

该类型Arc<RwLock<T>>可能一个引用。因为它是通用的并且T尚未定义。当您尝试将其与dynthen一起使用时,它将T成为参考,尽管与普通参考并不完全相同。

Rust By Example这里有一个简单的解释

要解决这个问题,您可以按照编译器的建议更改T: Node,T: Node + 'static,或者您可以将您dyn NodeRefCell.

于 2020-05-10T14:00:51.203 回答