考慮以下代碼:
trait MyTrait<'a, 'b: 'a> {
fn f(&'b mut self) -> &'a str;
}
struct MyStruct {
my_string: String,
}
impl<'a, 'b: 'a> MyTrait<'a, 'b> for MyStruct {
fn f(&'b mut self) -> &'a str {
&self.my_string
}
}
fn foo<'a, 'b: 'a>(s: &'b mut impl MyTrait<'a, 'b>) {
bar(s);
bar(s); // ERROR
}
fn foo2<'a>(s: &mut MyStruct) {
bar(s);
bar(s);
}
fn bar<'a, 'b: 'a>(s: &'b mut impl MyTrait<'a, 'b>) {
let x = s.f();
}
trait定義了一個回傳參考MyTrait的函式。str它的生命周期可以解釋為“回傳str參考的物件將至少與該參考一樣長。” .
bar是一個函式,它接受對實作MyTrait和呼叫的結構的可變參考f。函式foo和foo2相同,唯一的區別是foo2接受特定型別而foo接受任何實作的具體型別MyTrait。
自foo2編譯以來,我希望foo也能編譯,但事實并非如此。這是錯誤訊息:
error[E0499]: cannot borrow `*s` as mutable more than once at a time
--> src\bin\main2.rs:17:6
|
15 | fn foo<'a, 'b: 'a>(s: &'b mut impl MyTrait<'a, 'b>) {
| -- lifetime `'b` defined here
16 | bar(s);
| ------
| | |
| | first mutable borrow occurs here
| argument requires that `*s` is borrowed for `'b`
17 | bar(s); // ERROR
| ^ second mutable borrow occurs here
據我了解,這是正在發生的事情:MyTrait強制實作它的物件終生存在'b。終生第一次呼叫barborrows ,這意味著借用不會像常規借用一樣在結束時下降,而只會在 結束時下降。并且具有相同的函式簽名,因此指定的生命周期將是相同的。這意味著當可變借用時,這個借用只會在 then end of 時超出范圍。第二次呼叫將遵循相同的邏輯,但是由于已經存在可變借用,編譯器將不允許創建另一個借用。s'bbar'bfoobarbars'bfoobar
最初我認為一個解決方案是將簽名重寫bar為:
fn bar<'a, 'b: 'a, 'c: 'b>(s: &'b mut impl MyTrait<'a, 'c>)
希望編譯器將其解釋為:“'b僅在函式的持續時間記憶體在'a,甚至會受到進一步限制,并且物件本身的存在時間會超過函式*s的生命周期'c” 。但是它導致了錯誤:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
是否可以將借入限制bar為僅在 的期間記憶體在bar?
uj5u.com熱心網友回復:
我不確定你是如何得到你發布的代碼的,但我發現最好的經驗法則是從沒有明確的生命周期引數開始,只有在編譯器說它需要它們時才添加它們。
在您的情況下,編譯器不需要生命周期引數,很高興自己弄清楚它們:
trait MyTrait {
fn f(&mut self) -> &str;
}
struct MyStruct {
my_string: String,
}
impl MyTrait for MyStruct {
fn f(&mut self) -> &str {
&self.my_string
}
}
fn foo(s: &mut impl MyTrait) {
bar(s);
bar(s);
}
fn foo2(s: &mut MyStruct) {
bar(s);
bar(s);
}
fn bar(s: &mut impl MyTrait) {
let x = s.f();
}
特別是,對于任何獲取一個參考并回傳一個參考的函式,編譯器可以計算出回傳的參考必須來自引數,因此不需要明確的界限。它實際上在做什么(AFAIK)基本上是插入這些生命周期:
trait MyTrait {
fn f<'a>(&'a mut self) -> &'a str;
}
impl MyTrait for MyStruct {
fn f<'a>(&'a mut self) -> &'a str {
&self.my_string
}
}
請注意(1),生命周期是在函式上而不是在特征上。這是添加生命周期引數時的另一個好的經驗法則,始終將它們放在最具體的范圍內。另外(2),不需要兩個引數。
編輯:
至于為什么它不特別喜歡你的版本,我認為這基本上是因為生命周期是在特征而不是函式上定義的。特別是生命周期'b是typeMyTrait的一個特性,而不是任何特定代碼路徑的特性。就所有編譯器所知,對于泛型,這些生命周期可能與代碼中其他地方的其他東西完全相關,它無法知道不能超過您的呼叫,因為它必須在所有可能的 s 上都是泛型的。MyTrait<'a, 'b>'bfoo'b
但是foo2,編譯器可以看到 的所有生命周期MyStruct,它可以將這些生命周期折疊到它擁有的具體情況,并且知道它只需要在s的持續時間內借用bar。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/460176.html
