2015-07-05 97 views
5

TL; DR如何在引用共享基礎數據的泛型類型上構建數據結構?通用類型,所有權和持久數據結構

這個問題是關於鏽的語義和良好的數據建模。下面的代碼是我的問題(更)微不足道的蒸餾,突出我的具體問題,而不是我的實際代碼。

目標是創建一個函數,該函數可以構建幾個包含對泛型類型的共享數據的引用的向量。在下面的例子的命名中,我希望能夠返回可存儲Struct1Struct2(由特性Trait提取)的矢量集合,但是因爲(在我的真實代碼中)Struct1Struct2是相對很大,並且會比較頻繁地存儲在相對較多的地方,我寧願存儲對共享數據的引用,而不是將它們複製到整個地方。

我面對的(而且發生了許多中間修訂)目前的問題是:

  1. 由於數據的,我嘗試存儲類型是Trait,這是不Sized我需要保存在我的向量
  2. 引用由於每個結構將從多個向量引用,一個&參考是必要
  3. 哲學上,有一個給定結構的無奇異的「主人」,因此沒有「正確」的地方粘我的結構,使他們不會與的範圍功能。
    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 

回答

6

這聽起來像一個完美的使用情況爲RcRc是一個參考計數類型,允許您擁有多個所有者具有相同的值。

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)「,並導致了終身的錯誤。

+0

你正在恢復我對Rust的信仰,我感到有點恐慌。很好的答案。 – user12341234