好長時間沒寫博客了,現在在上海開始上班,算是新的開始。
今天隨便讀了一下Rust死靈書,記錄一下自己的領悟。
1let mut data = vec![1, 2, 3];
2let x = &data[0];
3data.push(4);
4println!("{}", x);
1'a: {
2 let mut data: Vec<i32> = vec![1, 2, 3];
3 'b: {
4 // 'b 這個生命週期範圍如我們所願地小(剛好夠 println!)
5 let x: &'b i32 = Index::index::<'b>(&'b data, 0);
6 'c: {
7 // 這裡有一個臨時作用域,我們不需要更長時間的 &mut 借用
8 Vec::push(&'c mut data, 4);
9 }
10 println!("{}", x);
11 }
12}
這裡的問題更微妙、更有趣。我們希望 Rust 拒絕這個程序,理由如下:我們有一個存活的共享引用
x到data的一個子集,當我們試圖把data的可變引用傳給push時。這將創建一個可變引用的別名,而這將違反引用的第二條規則。
然而,這根本不是 Rust 認為這個程序有問題的原因。Rust 不理解
x是對data的一個子集的引用。它根本就不理解Vec。它看到的是,x必須在'b範圍內保持存活才能被打印;接下來,Index::index的簽名要求我們對data的引用必須在'b範圍內存活。當我們試圖調用push時,它看到我們試圖構造一個&'c mut data。Rust 知道'c包含在'b中,並拒絕了我們的程序,因為&'b data必然還存活著!
在這裡我們看到,和我們真正想要保證的引用規則語義相比,生命週期系統要粗略得多。在大多數情況下,這完全沒問題,因為它使我們不用花整天的時間向編譯器解釋我們的程序。然而,這確實意味著有部分程序對於 Rust 的真正的語義來說是完全正確的,但卻被拒絕了,因為 lifetime 太傻了。
上面這一段是生命週期的一個例子,我愣是看了好長時間才懂了下面說的是什麼。
簡單來說上面的代碼違反了Rust的一個引用規則:不可變引用可以存在多個,但是可變引用只能存在一個。
x在b區域內被創建,為一個不可變引用,但是data.push創造了一個可變引用,所以這裡無法通過編譯。
解決方法也很簡單,把println!("{}", x);往上面調整一行就可以了,這樣x不再使用,rust就認為x的生命週期已經結束。