1. 程式人生 > >雜記rust的destructuring binding(反結構化繫結)與ownership(所有權)

雜記rust的destructuring binding(反結構化繫結)與ownership(所有權)

起因

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函式宣告順序可以隨意)