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

use*_*234 5 rust

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

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

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

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

  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));
}
Run Code Online (Sandbox Code Playgroud)

所需的输出是:

vec1: Struct1
vec2: Struct1 Struct2
Run Code Online (Sandbox Code Playgroud)

Fra*_*gné 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));
}
Run Code Online (Sandbox Code Playgroud)

需要注意的是在封闭shallow_copy使用clone()克隆Rc.克隆a Rc会创建一个Rc指向相同值的新值(克隆基础值),并且共享引用计数增加1.删除Rc引用计数的递减,并且当引用计数降为零时,基础值为下降.

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

此外,我不得不使用生命周期来注释特征对象,因为没有注释,编译器会假设'static(即Trait + 'static),这意味着"它的实现Trait不包含任何借用的指针(短于'static)",这导致了生命周期错误.