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

不幸的是,当连接节点/边缘时,我收到一个编译错误the parameter type 'T' may not live long enough(Playground)。

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>>有关?

最佳答案

编译错误说明了如何解决此问题:

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());
  }
}

playground

但是,为什么当我的T永远不会成为引用时,我需要终生限制吗?你问。好吧,Rust编译器还不知道,T可以是任何类型,包括引用。 T表示的类型集包括&T&mut T表示的类型集。 &T&mut T都是T的子集。这就是为什么必须在T上加上生命周期的原因,这是与编译器进行通信的方式,您的T将仅是拥有的类型或静态引用。

有关“静态生命周期”的更多信息
'static是一生中具有误导性的名称,因为它使大多数人认为'static类型必须在程序的整个过程中都有效,并且不能动态分配或删除。在现实中,这些都不是正确的:'static类型可以动态分配,也可以删除。实际上,'static的真正含义是“您可以无限期安全地保留此类型”。像StringVec这样的所有“自有类型”都是'static。这是一个Rust程序,我希望可以说明这一点:
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
    }
}

playground

有关生存期省略和默认特征对象生存期的更多信息

您可以这样定义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>>转换为NodeRef时,那么T必须以Node + 'static为边界,因为NodeRef也具有这些边界,即Arc<RwLock<dyn Node + 'static>>。 Rust中的所有特征对象都有生命周期,但是您通常不编写它们,因为Rust会为您推断它们。 The Rust Reference has a thorough explanation on lifetime elision and default trait object lifetimes如果您想了解更多。

您可以通过使类型别名优于'static来减轻'a的要求:
pub type NodeRef<'a> = Arc<RwLock<dyn Node + 'a>>;
pub type EdgeRef<'a> = Arc<Mutex<dyn Edge + 'a>>;

但是,这将极大地增加代码的复杂性,而且我敢肯定您想坚持使用'static,因为它已经支持了您要尝试执行的操作。

10-04 21:57
查看更多