雜記rust的destructuring binding(反結構化繫結)與ownership(所有權)
阿新 • • 發佈:2018-12-22
起因
看rust by example看得我想睡覺...突然遇到個關於反結構化繫結的奇怪的特性:
struct Pair(Box<i32>, Box<i32>); impl Pair { fn destroy(self) { let Pair(first, second) = self; println!("Destroying Pair({}, {})", first, second); } } fn main() { let pair = Pair(Box::new(1), Box::new(2)); pair.destroy(); pair.destroy(); }
對pair呼叫兩次destroy()
方法居然會得到錯誤,程式碼註釋提示let
語句會消耗self,導致self內容被move(移動):
let Pair(first, second) = self;
// 這裡self不再可見
以前聽說過rust的lifetime
,ownership
,看著樣子估計就是這方面的問題導致的特性。
嘗試
我大概理解了它的行為,let
反結構化繫結有點類似於c++的std::move()
?於是做了點實驗。
#[derive(Debug)] struct A{ x:i32, y:i32 } impl A{ fn new()->A { return A{x:123,y:345}; } } #[allow(unused_variables)] fn main(){ let a = A::new(); let A{x:pointx,y:pointy} = a; let A{x:pointx,y:pointy} = a; }
很遺憾,對a執行兩次反結構化繫結並沒有出現內容被移動。考慮到之前Pair裡面不是primitive type ,那隻能試著引入非primitive type:
#[derive(Debug)] struct A{ x:i32, y:i32 } struct B{ val:A } impl A{ fn new()->A { return A{x:123,y:345}; } } #[allow(unused_variables)] fn main(){ let a = A::new(); let A{x:pointx,y:pointy} = a; let A{x:pointx,y:pointy} = a; let b = B{val:a}; let B{val:res}=b; let B{val:res}=b; }
這次就如之前一樣,對b兩次反繫結得到錯誤,提示b.val已經被移動了。
error[E0382]: use of moved value: `b.val`
原因
然後試著rustc --explain E0382
得到了一個很長的解釋:
該錯誤是因為嘗試使用一個變數,但是變數的內容已經被移到了其他地方。
比如:
struct MyStruct { s: u32 }
fn main() {
let mut x = MyStruct{ s: 5u32 };
let y = x;
x.s = 6;
println!("{}", x.s);
}
MyStruct
是一個沒有被標記為Copy的型別,當我們let y = x
時,x的資料被移了出去。
這也是Rust所有權系統的基礎:一旦出了工作區,變數的值不能被兩個及以上的變數擁有。
有時候我們不需要移動這個值,那麼可以使用引用想另一個函式borrow(借)這個值,同時又不改變它的所有權。比如像下面這樣,不需要把值移動到calculate_length
函式裡面,就可以給引數加上引用:
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
(tips:rust函式宣告順序可以隨意)