通用类型,所有权和持久数据结构

时间:2015-07-05 04:54:34

标签: rust

TL; DR 如何在引用共享底层数据的泛型类型上构建数据结构?

这个问题与Rust中的语义和良好的数据建模有关。下面的代码是对我的问题的一个(更多)微不足道的提炼,以突出我的具体问题,而不是我的实际代码。

目标是创建一个函数,构建几个包含对泛型类型共享数据的引用的向量。在以下示例的命名法中,我希望能够返回可以存储Struct1Struct2的向量集合(由特征Trait抽象),但是因为(在我的真实代码中)Struct1Struct2相对较大并且将相对频繁地存储在相对较多的地方,我宁愿存储对共享数据的引用,而不是将它们全部复制到那里。

我面临的当前问题(并且有许多中间修订)是:

  1. 由于我试图存储的数据类型是Trait,而不是Sized我需要在我的向量中存储引用
  2. 由于每个结构都将从多个向量引用,因此需要&引用
  3. 哲学上,给定结构没有单一的“所有者”,因此没有“正确”的地方来粘贴我的结构,因此它们不会超出build_vectors函数的范围。
    1. 我考虑通过Traits的全局向量来解决这个问题,我可以在其中指出引用,遗憾的是上面的问题(1)似乎排除了该策略。
  4. struct Struct1;
    struct Struct2;
    
    trait Trait { fn name(&self) -> &str; }
    impl Trait for Struct1 { fn name(&self) -> &str { "Struct1" } }
    impl Trait for Struct2 { fn name(&self) -> &str { "Struct2" } }
    
    fn shallow_copy<'a>(v: &'a Vec<&'a Box<Trait>>) -> Vec<&'a Box<Trait>> {
       v.iter().map(|x|*x).collect()
    }
    
    fn build_vectors<'a>() -> (Vec<&'a Box<Trait>>, Vec<&'a Box<Trait>>) {
      let box_struct1: &Box<Trait> = &(Box::new(Struct1) as Box<Trait>);
      let box_struct2: &Box<Trait> = &(Box::new(Struct2) as Box<Trait>);
    
      let vec1: Vec<&Box<Trait>> = vec![box_struct1];
      let mut vec2: Vec<&Box<Trait>> = shallow_copy(&vec1);
    
      vec2.push(box_struct2);
    
      (vec1, vec2)
    }
    
    fn join_names(v: &Vec<&Box<Trait>>) -> String {
       v.iter().map(|s| s.name()).collect::<Vec<_>>().connect(" ")
    }
    
    fn main() {
      let (vec1, vec2) = build_vectors();
    
      println!("vec1: {}", join_names(&vec1));
      println!("vec2: {}", join_names(&vec2));
    }
    

    所需的输出是:

    vec1: Struct1
    vec2: Struct1 Struct2
    

1 个答案:

答案 0 :(得分:6)

这听起来像Rc的完美用例。 Rc是一种引用计数类型,允许您将多个所有者设为相同的值。

use std::rc::Rc;

struct Struct1;
struct Struct2;

trait Trait { fn name(&self) -> &str; }
impl Trait for Struct1 { fn name(&self) -> &str { "Struct1" } }
impl Trait for Struct2 { fn name(&self) -> &str { "Struct2" } }

fn shallow_copy<'a>(v: &[Rc<Trait + 'a>]) -> Vec<Rc<Trait + 'a>> {
   v.iter().map(|x| x.clone()).collect()
}

fn build_vectors() -> (Vec<Rc<Trait>>, Vec<Rc<Trait>>) {
  let vec1: Vec<Rc<Trait>> = vec![Rc::new(Struct1)];
  let mut vec2: Vec<Rc<Trait>> = shallow_copy(&vec1);

  vec2.push(Rc::new(Struct2));

  (vec1, vec2)
}

fn join_names<'a>(v: &[Rc<Trait + 'a>]) -> String {
   v.iter().map(|s| s.name()).collect::<Vec<_>>().connect(" ")
}

fn main() {
  let (vec1, vec2) = build_vectors();

  println!("vec1: {}", join_names(&vec1));
  println!("vec2: {}", join_names(&vec2));
}

请注意,shallow_copy中的结尾使用clone()来克隆Rc。克隆Rc会创建一个指向相同值的新Rc(基础值克隆),共享引用计数增加1.删除{ {1}}递减引用计数,当引用计数降为零时,基础值将被删除。

顺便说一句,我已经改变了一些函数来获取切片引用而不是Rc引用,因为它使函数更通用(例如,你也可以从数组中获取切片)。

另外,我必须使用生命周期来注释特征对象,因为没有注释,编译器会假定Vec(即'static),这意味着&#34; {{1}的实现}它不包含任何借用的指针(短于Trait + 'static)&#34;,这会导致生命周期错误。