Rust 中 Copy 和 Clone 特徵的區別

Copy 特徵和 Clone 特徵是 Rust 編程語言中的重要概念。從理論上講,它們很簡單,但在現實中很難應用,特別是對於來自 Python、Java 等語言的新程序員。在這篇文章中,我們將瞭解在 Rust 中 Copy 和 Clone 意味着什麼。

Rust 的所有權

在討論 rust 中的 Copy 和 Clone 特徵之前,讓我們先簡要了解一下所有權。在每一種編程語言中,都有在執行時管理計算機內存的方法。支持垃圾收集器的語言在程序執行時定期查找不再使用的內存,而在 C/C++ 等其他語言中,程序員必須顯式地分配和釋放內存。

在 Rust 中,內存通過所有權來管理。所有權是編譯器檢查、管理內存的一組規則。如果違反了任何所有權規則,程序甚至不會編譯。

所有權規則

因爲所有權對許多程序員來說是一個新概念,需要一些時間來適應,但是記住這些規則將有助於編寫安全和高效的代碼。

1,在 Rust 中,每個值都有一個所有者,即賦值給它的變量。

2,一次只能有一個所有者。雖然,多個變量可以保存對相同值的引用或借用相同值,但該值只有一個所有者。

3,當所有者超出範圍時,該值將被刪除。

Rust 中的內存分配 (堆棧 vs 堆)

Rust 在運行時同時提供堆棧和堆內存部分。在 Rust 中,理解內存分配是很重要的,因爲值如何存儲在內存中會影響語言的行爲,並幫助程序員做出某些決定。

壓入堆棧比在堆上分配快,因爲分配器不必尋找存儲新數據的地方。

像 u16、u32、i32 和 f64 這樣的值都存儲在堆棧中,因爲它們的大小在編譯時是已知的。

動態大小類型 (如 String 和 Vec) 存儲在堆中,因爲它們的大小可以增加或縮小,並且沒有固定的大小。

Rust 中的 Clone 特徵

在 Rust 中,一些簡單的類型是 “隱式可複製的”,當你分配它們或將它們作爲參數傳遞時,接收者將獲得一個副本,原始值保留在適當的位置。對於其他類型,必須通過實現 Clone 特徵並調用 Clone() 方法顯式地進行復制。

Clone 特徵定義了顯式創建對象 T 的深度拷貝的能力。當我們爲類型 T 調用 Clone 時,它會執行創建新 T 所需的所有任意複雜操作。

fn main() {
    let name = String::fron("IntMain!");
    // value的所有權由name轉移到new_name 
    let new_name = name;
    // 錯誤:name被移動!
    println!("Old name = {} and new name ={}", name, new_name);
}

在上面的例子中,根據所有權規則,name 分配給 new_name,將 String 實例的所有權轉移到 new_name,由於只能有一個所有者,因此 name 被刪除。因此,爲了獲得一個深度拷貝,我們需要顯式地調用 clone() 方法。

fn main() { 
    let name = String::fron("IntMain!"); 

    // 將name的深度拷貝分配給new_name
    // 對象的顯式複製 
    let new_name = name.clone(); 

    // 編譯良好,沒有錯誤
    println!("Old name = {} and New name ={}", name, new_name); }
}

由於深度拷貝,name 和 new_name 都可以獨立地刪除它們的堆緩衝區。

注意:Clone 特徵並不總是創建深度副本。類型可以自由地以任何它們想要的方式實現 Clone。例如,Rc 和 Arc 只增加一個引用計數,而不是創建一個深度副本。

Rust 中的 Copy 特徵

rust 中的 Copy 特徵定義了隱式複製對象的能力。Copy 行爲不可重載,它總是一個簡單的位拷貝。這適用於具有固定大小且完全存儲在堆棧上的類型。

fn main() { 
    let num = 987; 
    // new_num是num的副本 
    let new_num = 987; 

    //編譯良好,沒有錯誤
    println!("Old num = {} and New num ={}", nun, new_num); }
}

上面的例子並不與所有權規則相矛盾,因爲 i32 是 Copy 類型,因爲它們存儲在堆棧中,很容易複製它們,因此 num 的值被隱式複製到 new_num。正如我們可以想象的那樣,不是每一種類型都可以實現 Copy,最好的例子是 String,它實現了 Clone 而不是 Copy。

Copy 類型僅應用於存儲在堆棧中的類型,如整數、浮點數、及其所有字段都是 Copy 類型的 Struct。

// 這個結構實現了Copy特性
// 它的所有字段都是Copy類型
#[derive(Clone, Copy)]
struct copy_and_cloneable{
    num1: i32,
    num2: i32,
    num3: bool,
}

// 這個結構沒有實現Copy trait
// String不是Copy類型
#[derive(Clone, Copy)]
stuct cloneable_only{
    num1: i32,
    name: String,     //Not a Copy type
}

注意:Clone 是 Copy 的 super trait,所以所有實現 Copy 的都必須實現 Clone,因此當一個類型實現 Copy 時,Clone 實現只是返回 * self。

總結

在 rust 中,一旦你理解了 rust 的內存分配,就很容易理解 Copy 和 Clone 之間的區別。可推入堆棧的類型實現了 Copy 特徵,可推入堆的類型實現了 Clone 特徵。由於深度複製是昂貴的,因此我們需要顯式調用 clone() 方法,否則默認情況下發生移動。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/AvyenYcDMAtNP0aLFqlUFw