RefCell<T>
包含UnsafeCell<T>
這是一個特殊的lang item。導致錯誤的是UnsafeCell
。你可以用檢查:
fn error<'a>(foo: &UnsafeCell<Option<&'a mut String>>, s: &'a mut String) {}
...
let bar = UnsafeCell::new(None);
error(&bar, &mut s);
但誤差不會由於編譯器識別的UnsafeCell引入內部可變性,但是,一個UnsafeCell
是invariant在T.其實,我們可以使用PhantomData重現錯誤:
struct Contravariant<T>(PhantomData<fn(T)>);
fn error<'a>(foo: Contravariant<&'a i32>, s: &'a mut String) {}
...
let bar = Contravariant(PhantomData);
error(bar, &mut s);
甚至只是東西都在一生'a
逆變或不變:
fn error<'a>(foo: Option<fn(&'a i32)>, s: &'a mut String) {}
let bar = None;
error(bar, &mut s);
您無法隱藏RefCell的原因是因爲方差是通過結構的字段派生的。一旦你在某處使用了RefCell<T>
,無論多深,編譯器都會計算出T
是不變的。
現在讓我們看看編譯器如何確定E0502錯誤。首先,重要的是要記住,編譯器必須在這裏選擇兩個特定的生命週期:表達式&mut s
('a
)類型中的生命週期以及bar
(我們稱之爲'x
)類型的生命週期。兩者都是受限制的:前者的使用壽命'a
必須小於s
的範圍,否則我們最終會得到比原始字符串壽命更長的參考。 'x
必須大於bar
的範圍,否則我們可以通過bar
訪問懸掛指針(如果某個類型具有生存期參數,則編譯器假定該類型可以訪問具有該生存期的值)。
有了這兩個基本的限制,編譯器經過以下步驟:
- 類型
bar
是Contravariant<&'x i32>
。
- 的
error
函數接受的Contravariant<&'a i32>
任何亞型,其中'a
是&mut s
表達的壽命。
- 因此
bar
應的Contravariant<&'a i32>
Contravariant<T>
亞型超過T
逆變,即如果U <: T
,則Contravariant<T> <: Contravariant<U>
。
- 因此,當
&'x i32
是超類型的&'a i32
時可以滿足子類型關係。
- 因此
'x
應短比'a
,即'a
應活得比'x
。
類似地,對於不變量類型,所導出的關係是'a == 'x
,以及用於convariant,'x
會超越'a
。現在
,這裏的問題是,壽命在類型bar
生命直到範圍的端部(按上面提到的限制):
let bar = Contravariant(PhantomData); // <--- 'x starts here -----+
error(bar, // |
&mut s); // <- 'a starts here ---+ |
s.len(); // | |
// <--- 'x ends here¹ --+---+
// |
// <--- 'a ends here² --+
}
// ¹ when `bar` goes out of scope
// ² 'a has to outlive 'x
在這兩個逆變和不變的情況下,'a
會超越(或等於)'x
表示語句s.len()
必須包含在範圍內,從而導致借位錯誤。
只有在協變的情況下,我們可以做的'a
短的射程比'x
,讓s.len()
被稱爲前的臨時對象&mut s
被丟棄(意思是:在s.len()
,s
不再被認爲借):
let bar = Covariant(PhantomData); // <--- 'x starts here -----+
// |
error(bar, // |
&mut s); // <- 'a starts here --+ |
// | |
// <- 'a ends here ----+ |
s.len(); // |
} // <--- 'x ends here -------+
哦,我的!多麼驚人的答案!我已經擔心答案只會是「'RefCell'特別」,但這是一個了不起的見解。謝謝♥有一個問題,但:在'foo:Option'的情況下,我們能否真正打破記憶安全?還是由於編譯器如何在內部認爲它基本上是一個誤報? –
@LukasKalbertodt如果這是額外的解釋,而不是問題,請隨意。 (如果還有其他相關問題,評論框太小,請改爲編輯問題)。 – kennytm
@LukasKalbertodt我相信'Option'案件是一個誤判,可以用非詞彙的生命週期來解決。但我沒有詳細檢查,也許它會與線程有一些不好的交互。 –
kennytm