2016-01-25 12 views
-1

我終於決定給Rust(1.7 & 1.8)一試。來自C++,我必須說Rust看起來很棒。我試圖在C++中重現一個衆所周知的行爲,其中包括在一組對象上使用動態多態。在指定自我生命期時使用特質上的動態多態性使用Rust的問題

我經歷了一個非常煩人的問題,我能解決,但我想知道你們對這個問題的看法,我希望它可以幫助其他人試圖做同樣的事情。

讓我們看看下面的代碼實現了最初的想法:

struct Foo 
{ 
    foo: u32, 
} 
trait Bar 
{ 
    fn bar(& self) -> u32; 
} 
impl Bar for Foo 
{ 
    fn bar(& self) -> u32 
    { 
     return self.foo; 
    } 
} 
fn foobar(l: &std::collections::LinkedList<& Bar>) 
{ 
    for i in l 
    { 
     println!("{}", i.bar()); 
    } 
} 
fn main() 
{ 
    let foo0 = Foo{foo: 8u32}; 
    let foo1 = Foo{foo: 8u32}; 
    let mut l = std::collections::LinkedList::new(); 
    l . push_back(&foo0 as &Bar); 
    l . push_back(&foo1 as &Bar); 
    foobar(&l); 
} 

這裏的一切編譯,一切都可以正常使用。

我想通過另一個參考功能bar的性狀Bar,因此我不得不增加一生的Bar性狀。爲了簡單起見,我將添加生命週期(因爲它將在Rust 1.8下很好地編譯)。通過整個代碼添加使用壽命後,代碼最終看起來像這樣:

struct Foo 
{ 
    foo: u32, 
} 
trait Bar<'a> 
{ 
    fn bar(& 'a self) -> u32; 
} 
impl<'a> Bar<'a> for Foo 
{ 
    fn bar(& 'a self) -> u32 
    { 
     return self.foo; 
    } 
} 
fn foobar<'a>(l: &std::collections::LinkedList<& 'a Bar>) 
{ 
    for i in l 
    { 
     println!("{}", i.bar()); 
    } 
} 
fn main() 
{ 
    let foo0 = Foo{foo: 8u32}; 
    let foo1 = Foo{foo: 8u32}; 
    let mut l = std::collections::LinkedList::new(); 
    l . push_back(&foo0 as &Bar); 
    l . push_back(&foo1 as &Bar); 
    foobar(&l); 
} 

如果你編譯這段代碼,錯誤信息將在以下幾點:

../test.rs:21:12: 21:13 error: cannot infer an appropriate lifetime due to conflicting requirements [E0495] 
../test.rs:21 for i in l 
         ^
../test.rs:19:1: 25:2 help: consider using an explicit lifetime parameter as shown: fn foobar<'a>(l: &std::collections::LinkedList<&'a Bar>) 
../test.rs:19 fn foobar<'a>(l: &std::collections::LinkedList<& 'a Bar>) 
../test.rs:20 { 
../test.rs:21 for i in l 
../test.rs:22 { 
../test.rs:23  println!("{}", i.bar()); 
../test.rs:24 } 
       ... 
error: aborting due to previous error 

這裏的問題是清楚的,編譯器知道push_back中給出的變量具有不同的生命期,因此它不符合我所寫的內容。如果在相同的let語句中聲明變量foo0foo1,則可以解決問題。

我發現很難弄清楚那段代碼出了什麼問題。 我的問題是:

  • 有沒有辦法告訴集合可能會採取不同的生命週期的編譯器?

  • 通過在另一個引用變量(此處未顯示)上設置'a生命週期而不是self,代碼將進行編譯。這是否意味着如果我們沒有指定任何生命週期,那麼編譯器將'_與我在前一個問題中提到的具體生命週期相對應?

  • 有沒有隱含的規則,「禁止」我們設置自己的一生?

  • 這段代碼是否慣用Rust?

+0

http://codereview.stackexchange.com/可能更適合於這種問題... –

+0

請讓每[只有一個問題問題](http://meta.stackexchange.com/q/39223/281829)。在Code Review中可以找到「是否習慣用語」的答案,但請在此處移動您的問題之前查看[此帖子](http://meta.codereview.stackexchange.com/q/5777/32521)。也許你可以選擇其中一個功能性問題,並將你的問題重新編寫爲專注於它的開始? – Shepmaster

+3

@LukasKalbertodt:不,看起來並不是因爲1.)錯誤被提及,並且2.)所有'Foo'和'Bar's都表明這主要是假設代碼。 – Jamal

回答

3

如果你改變的foobar這個定義中的第二個代碼示例將編譯:

fn foobar<'a>(l: &std::collections::LinkedList<&'a Bar<'a>>) { 
    for i in l { 
     println!("{}", i.bar()); 
    } 
} 

但是,你終身參數做什麼,不使一個很大的意義對我來說。

當我們想要一個返回一個引用的方法時,我們通常定義一個在一生中通用的特徵(例如您的第二個版本的Bar),該引用的生命週期是實現者成員的生命週期。例如,假設我們有以下結構:

struct Foo<'a> { 
    foo: &'a str, 
} 

該結構包含一個引用,我們必須明確命名該生命週期。我們希望允許任何生命週期,所以我們定義一個生命週期參數'a

我們可以添加一個固有的方法來這個結構:

impl<'a> Foo<'a> { 
    fn foo(&self) -> &'a str { 
     self.foo 
    } 
} 

通知&self怎麼沒有明確的壽命。相反,我們在foo方法的返回類型上使用'a'a參數,因爲我們希望返回值的生命週期與結構中的生命週期相同,而不是self的生命週期(通常會更短)。

如果我們想要類似地定義特徵方法,該怎麼辦?

trait Bar<'a> { 
    fn bar(&self) -> &'a str; 
} 

impl<'a> Bar<'a> for Foo<'a> { 
    fn bar(&self) -> &'a str { 
     self.foo 
    } 
} 

的性狀Bar限定了壽命參數,並將其在impl鏈接的Foo壽命參數。

您也可以在個別方法上添加生命期參數,而不是在特徵上添加生命期參數。

例如,考慮這一特質:

trait Slice { 
    fn slice<'a>(&self, s: &'a str) -> &'a str; 
} 

我們想要的結果有相同的壽命爲s參數。爲此,我們需要在方法上定義一個生命週期參數並將其應用於兩個參考。

爲了完整起見,下面是特質的實現:

struct SliceBounds { 
    start: usize, 
    end: usize, 
} 

impl Slice for SliceBounds { 
    fn slice<'a>(&self, s: &'a str) -> &'a str { 
     &s[self.start..self.end] 
    } 
} 
+0

非常感謝弗朗西斯,我意識到把自己的一生放在自己身上絕對沒有任何意義,因爲它與此無關。我想我很困惑,因爲我們在「impl」這一行上設置了特性的一生。 –