【问题标题】:Generic types, ownership, and persistent data structures通用类型、所有权和持久数据结构
【发布时间】:2015-09-22 12:04:40
【问题描述】:

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

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

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

我目前面临的问题(并且有很多中间版本)是:

  1. 由于我要存储的数据类型是 Trait,而不是 Sized,我需要将引用存储在我的向量中
  2. 由于每个结构都将被多个向量引用,因此需要& 引用
  3. 从哲学上讲,给定结构没有单一的“所有者”,因此没有“正确”的位置来粘贴我的结构,因此它们不会超出build_vectors 函数的范围。
    1. 我考虑尝试通过Traits 的全局向量来解决这个问题,我可以在其中指向引用,不幸的是,上面的问题 (1) 似乎排除了该策略。

.

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

【问题讨论】:

    标签: rust


    【解决方案1】:

    这听起来像是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。删除 Rc 会减少引用计数,当引用计数降至零时,基础值将被丢弃。

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

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

    【讨论】:

    • 你恢复了我对 Rust 的信心,我有点慌了。很好的答案。
    猜你喜欢
    • 2013-04-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-07
    • 2017-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多