深入理解Rust所有權(quán)
所有權(quán)是 Rust 最獨特的特性,對語言的其他部分有著深刻的影響。它使 Rust 能夠在不需要垃圾收集器的情況下保證內(nèi)存安全,因此理解所有權(quán)是如何工作的很重要。在本文中,我們將討論所有權(quán)以及幾個相關的特性:借用、切片,以及 Rust 如何在內(nèi)存中布局數(shù)據(jù)。
什么是所有權(quán)?
所有權(quán)是一組控制 Rust 程序如何管理內(nèi)存的規(guī)則。
所有程序在運行時都必須管理它們使用計算機內(nèi)存的方式。一些語言有垃圾收集,在程序運行時定期查找不再使用的內(nèi)存;在其他語言中,程序員必須顯式地分配和釋放內(nèi)存。Rust 使用了第三種方法:內(nèi)存通過一個所有權(quán)系統(tǒng)進行管理,該系統(tǒng)擁有一組編譯器檢查的規(guī)則。如果違反了任何規(guī)則,程序?qū)o法編譯。
所有權(quán)的主要目的是管理堆數(shù)據(jù)。
跟蹤代碼的哪些部分正在使用堆上的哪些數(shù)據(jù),最小化堆上的重復數(shù)據(jù)量,以及清理堆上未使用的數(shù)據(jù),這樣就不會耗盡空間,這些都是所有權(quán)可以解決的問題。
所有權(quán)規(guī)則
首先,讓我們看一下所有權(quán)規(guī)則:
- Rust 中的每個值都有一個所有者。
 - 一次只能有一個所有者。
 - 當所有者超出范圍時,該值將被刪除。
 
變量作用域
作用域是程序中某項有效的范圍。變量從聲明它的地方開始有效,直到當前作用域結(jié)束。
示例:
    {                      // s is not valid here, it's not yet declared
        let s = "hello";   // s is valid from this point forward
        // do stuff with s
    }                      // this scope is now over, and s is no longer valid
這里有兩個重要的時間點:
- 當 s 進入作用域時,它是有效的。
 - 它在超出作用域之前一直有效。
 
在這一點上,作用域和變量何時有效之間的關系與其他編程語言中的關系類似。
引子:字符串變量和字面量
我們已經(jīng)看到了字符串字面量,其中字符串值被硬編碼到程序中。字符串字面值很方便,但它們并不適合我們可能想要使用文本的所有情況。一個原因是它們是不可變的。另一個問題是,當我們編寫代碼時,不是每個字符串值都是已知的。
對于這些情況,可以創(chuàng)建字符串變量:
    let mut s = String::from("hello");
    s.push_str(", world!"); // push_str() appends a literal to a String
    println!("{s}"); // This will print `hello, world!`
這種類型的字符串可以被改變。
為什么字符串可以改變,而字面量不能?不同之處在于這兩種類型處理內(nèi)存的方式。
內(nèi)存和分配
對于字符串字面值,我們在編譯時就知道其內(nèi)容,因此文本直接硬編碼到最終的可執(zhí)行文件中。這就是字符串字面值快速高效的原因。但這些屬性僅來自字符串文字的不變性。不幸的是,我們不能為每個在編譯時大小未知且在運行程序時大小可能改變的文本塊放入二進制文件中的內(nèi)存塊。
對于 String 類型,為了支持可變的、可增長的文本片段,我們需要在堆上分配一定數(shù)量的內(nèi)存(在編譯時未知)來保存內(nèi)容。這意味著:
- 內(nèi)存必須在運行時從內(nèi)存分配器請求。
 - 我們需要一種方法,在使用完 String 后將這些內(nèi)存返回給分配器。
 
第一部分由我們完成,當調(diào)用String::from時,它的實現(xiàn)請求它所需的內(nèi)存。這在編程語言中是非常普遍的。
然而,第二部分是不同的。在帶有垃圾收集器(GC)的語言中,GC 跟蹤并清理不再使用的內(nèi)存,我們不需要考慮它。
在大多數(shù)沒有 GC 的語言中,我們有責任識別內(nèi)存何時不再被使用,并調(diào)用代碼顯式釋放它,就像我們請求它一樣。正確地做到這一點歷來是一個困難的編程問題。如果我們忘記了,我們就會浪費記憶。如果我們做得太早,就會得到一個無效的變量。如果我們做兩次,這也是一個 bug。我們需要恰好配對一個已分配的和一個空閑的。
Rust 采用不同的路徑:一旦擁有內(nèi)存的變量超出作用域,內(nèi)存就會自動返回。
    {
        let s = String::from("hello"); // s is valid from this point forward
        // do stuff with s
    }
    // this scope is now over, and s is no longer valid
當變量超出作用域時,Rust 會為我們調(diào)用一個特殊的函數(shù)。這個函數(shù)被稱為 drop,該函數(shù)將內(nèi)存返回給分配器。
注意:在 C++ 中,這種在項目生命周期結(jié)束時釋放資源的模式有時被稱為資源獲取即初始化(RAII)。
與 Move 交互的變量和數(shù)據(jù)
在 Rust 中,多個變量可以以不同的方式與相同的數(shù)據(jù)交互。
示例 1:
    let x = 5;
    let y = x;
我們現(xiàn)在有兩個變量,x 和 y,它們都等于 5。因為整數(shù)是具有已知的固定大小的簡單值,并且這兩個 5 值被壓入堆棧。
示例 2:
    let s1 = String::from("hello");
    let s2 = s1;
字符串由三部分組成:指向存儲字符串內(nèi)容的內(nèi)存的指針、長度和容量。長度是指字符串的內(nèi)容當前使用了多少內(nèi)存(以字節(jié)為單位)。容量是字符串從分配器接收到的總內(nèi)存量(以字節(jié)為單位)。
這組數(shù)據(jù)存儲在堆棧上,右邊是堆中保存內(nèi)容的內(nèi)存。

當將 s1 賦值給 s2 時,復制了 String 數(shù)據(jù),這意味著復制了堆棧上的指針、長度和容量。但是,不復制指針所指向的堆上的數(shù)據(jù)。

前面我們說過,當變量超出作用域時,Rust 會自動調(diào)用 drop 函數(shù)并為該變量清理堆內(nèi)存。但是上圖顯示兩個數(shù)據(jù)指針都指向同一個位置。這是一個問題:當 s2 和 s1 超出作用域時,它們都將嘗試釋放相同的內(nèi)存。這被稱為 double free error,是內(nèi)存安全錯誤之一。釋放內(nèi)存兩次可能會導致內(nèi)存損壞,這可能會導致安全漏洞。
為了確保內(nèi)存安全,在 let s2 = s1; 行之后,Rust 認為 s1 不再有效。因此,當 s1 超出作用域時,Rust 不需要釋放任何東西。
在創(chuàng)建 s2 之后嘗試使用 s1 會報錯:使用無效的引用。
在 C++ 中,你可能聽說過淺拷貝和深拷貝這兩個術(shù)語,那么在不復制數(shù)據(jù)的情況下復制指針、長度和容量的概念,可以視為淺拷貝。但是因為 Rust 會使第一個變量無效,所以它被稱為移動(Move),而不是淺拷貝。在這個例子中,我們會說 s1 被移動到 s2。

這就解決了我們的問題!只有 s2 有效,當它超出作用域時,它會單獨釋放內(nèi)存,這樣就完成了。
此外,這還隱含了一個設計選擇:Rust 永遠不會自動創(chuàng)建數(shù)據(jù)的“深度”副本。因此,就運行時性能而言,任何自動復制都可以被認為是廉價的。
范圍和分配
作用域、所有權(quán)和通過 drop 函數(shù)釋放的內(nèi)存之間的關系也是如此。
當你給一個已經(jīng)存在的變量賦一個全新的值時,Rust 會調(diào)用 drop 并立即釋放原始值的內(nèi)存。
示例:
    let mut s = String::from("hello");
    s = String::from("ahoy");
    println!("{s}, world!");
我們首先聲明一個變量 s,并將其綁定到一個值為 “hello” 的字符串。然后,我們立即創(chuàng)建一個值為 “ahoy” 的新 String,并將其賦值給 s。此時,原始字符串立即超出了作用域,Rust 運行 drop函數(shù)立即釋放 “hello” 的內(nèi)存。

克?。–lone)
Rust 提供一個叫 clone 的方法進行深拷貝。
示例:
    let s1 = String::from("hello");
    let s2 = s1.clone();
    println!("s1 = {s1}, s2 = {s2}");
堆數(shù)據(jù)確實被復制了。

只在棧上的數(shù)據(jù):復制(Copy)
示例:
    let x = 5;
    let y = x;
    println!("x = {x}, y = {y}");
這段代碼似乎與我們剛剛學到的內(nèi)容相矛盾:我們沒有調(diào)用 clone,但是 x 仍然有效,并且沒有移動到 y 中。
原因是,在編譯時具有已知大小的整數(shù)等類型完全存儲在堆棧中,因此可以快速復制實際值。這意味著我們沒有理由在創(chuàng)建變量 y 后阻止 x 的有效性。換句話說,這里沒有深度復制和淺復制的區(qū)別,所以調(diào)用 clone 與通常的淺復制沒有任何不同,我們可以省略它。
Rust 有一個特殊的注釋,叫做 Copy trait,我們可以把它放在存儲在堆棧上的類型上,就像整數(shù)一樣。如果一個類型實現(xiàn)了 Copy 特性,那么使用它的變量不會移動,而是被簡單地復制,使它們在賦值給另一個變量后仍然有效。
如果類型或其任何部分實現(xiàn)了 Drop 特性,Rust 將不允許我們用 Copy 注釋類型。如果該類型需要在值超出作用域時發(fā)生一些特殊的事情,并且向該類型添加 Copy 注釋,則會得到編譯時錯誤。
一般地,任何一組簡單標量值都可以實現(xiàn) Copy,并且不需要分配或某種形式的資源來實現(xiàn) Copy。下面是一些實現(xiàn) Copy 的類型:
- 所有整數(shù)類型,如 u32。
 - 所所有浮點類型,如 f64。
 - 所布爾類型,bool。
 - 所字符類型,char。
 - 只包含實現(xiàn) Copy 的類型的元組。例如,(i32, i32)實現(xiàn)了Copy,但(i32, String)沒有。
 
所有權(quán)與函數(shù)
將值傳遞給函數(shù)的機制類似于將值賦給變量的機制。將變量傳遞給函數(shù)會移動或復制,就像賦值一樣。
示例:
fn main() {
    let s = String::from("hello");  // s comes into scope
    takes_ownership(s);             // s's value moves into the function...
                                    // ... and so is no longer valid here
    let x = 5;                      // x comes into scope
    makes_copy(x);                  // because i32 implements the Copy trait,
                                    // x does NOT move into the function,
    println!("{}", x);              // so it's okay to use x afterward
} // Here, x goes out of scope, then s. But because s's value was moved, nothing
  // special happens.
fn takes_ownership(some_string: String) { // some_string comes into scope
    println!("{some_string}");
} // Here, some_string goes out of scope and `drop` is called. The backing
  // memory is freed.
fn makes_copy(some_integer: i32) { // some_integer comes into scope
    println!("{some_integer}");
} // Here, some_integer goes out of scope. Nothing special happens.
如果我們試圖在調(diào)用 takes_ownership 之后使用 s, Rust 會拋出一個編譯時錯誤。
返回值和作用域
返回值也可以轉(zhuǎn)移所有權(quán)。
示例:
fn main() {
    let s1 = gives_ownership();         // gives_ownership moves its return
                                        // value into s1
    let s2 = String::from("hello");     // s2 comes into scope
    let s3 = takes_and_gives_back(s2);  // s2 is moved into
                                        // takes_and_gives_back, which also
                                        // moves its return value into s3
} // Here, s3 goes out of scope and is dropped. s2 was moved, so nothing
  // happens. s1 goes out of scope and is dropped.
fn gives_ownership() -> String {             // gives_ownership will move its
                                             // return value into the function
                                             // that calls it
    let some_string = String::from("yours"); // some_string comes into scope
    some_string                              // some_string is returned and
                                             // moves out to the calling
                                             // function
}
// This function takes a String and returns one
fn takes_and_gives_back(a_string: String) -> String { // a_string comes into
                                                      // scope
    a_string  // a_string is returned and moves out to the calling function
}
變量的所有權(quán)每次都遵循相同的模式:將值賦給另一個變量會移動該變量。當包含堆上數(shù)據(jù)的變量超出作用域時,除非數(shù)據(jù)的所有權(quán)已移動到另一個變量,否則該值將通過 drop 清除。
Rust 允許我們使用元組返回多個值,雖然這是可行的,但是獲取所有權(quán)并返回每個函數(shù)的所有權(quán)有點繁瑣。
示例:
fn main() {
    let s1 = String::from("hello");
    let (s2, len) = calculate_length(s1);
    println!("The length of '{s2}' is {len}.");
}
fn calculate_length(s: String) -> (String, usize) {
    let length = s.len(); // len() returns the length of a String
    (s, length)
}
如果我們想讓一個函數(shù)使用一個值,但不獲得所有權(quán),該怎么辦?
幸運的是,Rust 有一個不用轉(zhuǎn)移所有權(quán)就能使用值的特性,叫做引用。
引用(References)和借用(Borrowing)
引用類似于指針,因為它是一個地址,我們可以按照它訪問存儲在該地址的數(shù)據(jù)。
引用用 & 符號表示。與使用 & 進行引用相反的是解引用,它是通過解引用操作符 * 完成的。
與指針不同,引用保證在其生命周期內(nèi)指向特定類型的有效值。
示例:
fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("The length of '{s1}' is {len}.");
}
fn calculate_length(s: &String) -> usize { // s is a reference to a String
    s.len()
} // Here, s goes out of scope. But because s does not have ownership of what
  // it refers to, the value is not dropped.
我們稱創(chuàng)建引用的操作為借用。&s1 語法允許我們創(chuàng)建一個引用 s,該引用引用 s1 的值,但不擁有該值。因為引用不擁有它,所以當引用停止使用時,它所指向的值不會被刪除。同樣,函數(shù)的定義使用 & 表示形參 s 的類型是引用。

引用也是不可變的。我們不允許修改我們引用過的東西。
示例:
fn main() {
    let s = String::from("hello");
    change(&s);
}
fn change(some_string: &String) {
    some_string.push_str(", world");
}
這段代碼會報錯:error[E0596]: cannot borrow `*some_string` as mutable, as it is behind a `&` reference。
Rust 通過“借用檢查器”確保引用的安全性。
變量對其數(shù)據(jù)有三種權(quán)限:
- 讀:數(shù)據(jù)可以被復制到另一個位置。
 - 寫:數(shù)據(jù)可以被修改。
 - 擁有:數(shù)據(jù)可以被移動或釋放。
 
這些權(quán)限在運行時并不存在,僅在編譯器內(nèi)部存在。
默認情況下,變量對其數(shù)據(jù)具有讀/擁有的權(quán)限,如果變量是可變的,那么它還擁有寫權(quán)限。
重點:引用可以臨時解除這些權(quán)限。
可變的引用
修改之前的代碼,使其允許我們通過使用可變引用的一些小調(diào)整來修改借用值。
fn main() {
    let mut s = String::from("hello");
    change(&mut s);
}
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}
用 &mut s 就可以創(chuàng)建一個可變引用。
可變引用有一個很大的限制:如果對一個值有一個可變引用,那么就不能有對該值的其他引用。下面的代碼試圖創(chuàng)建對 s 的兩個可變引用將會失敗:
    let mut s = String::from("hello");
    let r1 = &mut s;
    let r2 = &mut s;
    println!("{}, {}", r1, r2);
規(guī)定不能同時對同一數(shù)據(jù)進行多個可變引用的限制,這樣做的好處是 Rust 可以在編譯時防止數(shù)據(jù)競爭。數(shù)據(jù)競爭類似于競爭條件,發(fā)生在以下三種行為時:
- 兩個或多個指針同時訪問相同的數(shù)據(jù)。
 - 至少有一個指針被用來寫數(shù)據(jù)。
 - 沒有使用任何機制來同步對數(shù)據(jù)的訪問。
 
數(shù)據(jù)競爭會導致未定義的行為,當你試圖在運行時追蹤它們時,可能很難診斷和修復它們。Rust 通過拒絕編譯帶有數(shù)據(jù)競爭的代碼來防止這個問題!
我們可以使用花括號來創(chuàng)建一個新的作用域,允許多個可變引用,只是不能同時使用:
    let mut s = String::from("hello");
    {
        let r1 = &mut s;
    } // r1 goes out of scope here, so we can make a new reference with no problems.
    let r2 = &mut s;
Rust 對可變引用和不可變引用的組合強制了類似的規(guī)則:
- 對同一值有不可變引用的同時,也不能有可變引用。
 - 允許使用多個不可變引用。
 
示例:
    let mut s = String::from("hello");
    let r1 = &s; // no problem
    let r2 = &s; // no problem
    let r3 = &mut s; // BIG PROBLEM
    println!("{}, {}, and {}", r1, r2, r3);
報錯:error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable。
請注意,引用的作用域從它被引入的地方開始,一直持續(xù)到最后一次使用該引用的時候。例如,下面這段代碼可以編譯,因為不可變引用的最后一次使用是在 println!,在引入可變引用之前:
    let mut s = String::from("hello");
    let r1 = &s; // no problem
    let r2 = &s; // no problem
    println!("{r1} and {r2}");
    // variables r1 and r2 will not be used after this point
    let r3 = &mut s; // no problem
    println!("{r3}");
編譯器可以在作用域結(jié)束之前的某個點上判斷出引用不再被使用。
加深對可變引用的理解
可變引用提供對數(shù)據(jù)唯一的且非擁有的訪問。
不可變引用是只讀的??勺円迷诓灰苿訑?shù)據(jù)的情況下,臨時提供可變訪問。

可變引用臨時降級為只讀引用
對一個可變引用再進行引用,可以暫時剝奪可變引用的寫權(quán)限,新引用只具有讀權(quán)限。

權(quán)限在生命周期結(jié)束時被返回
在創(chuàng)建了 x 的引用 y 后,x 的寫和擁有權(quán)限暫時被剝奪,直到 y 的最后一次被使用之后,x 重新獲得寫和擁有權(quán)限。

懸空的引用
在使用指針的語言中,很容易通過釋放一些內(nèi)存而保留指向該內(nèi)存的指針來錯誤地創(chuàng)建懸空指針。
相比之下,在 Rust 中,編譯器保證引用永遠不會是懸空引用:如果你有對某些數(shù)據(jù)的引用,編譯器將確保數(shù)據(jù)不會在對數(shù)據(jù)的引用超出作用域之前超出作用域。
即,數(shù)據(jù)必須在其所有的引用存在的期間存活。
示例:創(chuàng)建了 s 的引用 s_ref 后, s 的擁有權(quán)限暫時被剝奪,于是不能移動或刪除,程序在 drop(s) 出報錯。

讓我們嘗試創(chuàng)建一個懸空引用,看看 Rust 是如何用編譯時錯誤來防止它們的:
fn main() {
    let reference_to_nothing = dangle();
}
fn dangle() -> &String {
    let s = String::from("hello");
    &s
}
報錯:error[E0106]: missing lifetime specifier。
這個錯誤消息涉及到一個我們尚未涉及的特性:生命周期。在這里我們先不討論這個特性,而是分析 dangle() 函數(shù)的錯誤原因:因為 s 是在 dangle 內(nèi)部創(chuàng)建的,所以當 dangle 的代碼完成時,s 將被釋放。但是我們試著返回對它的引用。這意味著這個引用將指向一個無效的字符串,于是發(fā)生了錯誤。
這里的解決方案是直接返回 String,而不是其引用:
fn no_dangle() -> String {
    let s = String::from("hello");
    s
}
總結(jié):
- 在任何給定的時間,可以有一個可變引用或任意數(shù)量的不可變引用。
 - 引用必須總是有效的。
 
接下來,我們來看看另一種類型的引用:切片。
切片(Slice)
切片允許引用集合中連續(xù)的元素序列,而不是整個集合。
切片是一種引用,所以它沒有所有權(quán)。
這里有一個小編程問題:編寫一個函數(shù),它接受一個由空格分隔的單詞字符串,并返回它在該字符串中找到的第一個單詞的長度。如果函數(shù)在字符串中沒有找到空格,則整個字符串必須是一個單詞,因此應該返回整個字符串的長度。
讓我們來看看如何在不使用切片的情況下編寫這個函數(shù),以理解切片將解決的問題:
fn first_word(s: &String) -> usize {
	// convert String to an array of bytes
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return i;
        }
    }
    s.len()
}
我們現(xiàn)在有一種方法來找出字符串中第一個單詞末尾的索引,但是有一個問題。我們自己返回了一個 usize,但它只是一個獨立于 String 的值,所以不能保證它在將來仍然有效。
示例:
fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s); // word will get the value 5
    s.clear(); // this empties the String, making it equal to ""
    // `word` still has the value `5` here, but `s` no longer has any content
    // that we could meaningfully use with the value `5`, so `word` is now
    // totally invalid!
}
這個程序編譯時沒有任何錯誤,如果在調(diào)用 s.clear() 之后使用 word,也不會出現(xiàn)任何錯誤。因為 word 根本沒有連接到 s 的狀態(tài),所以 word 仍然包含值 5。我們可以使用這個值 5 和變量 s 來嘗試提取出第一個單詞,但這將是一個 bug,因為自從我們將 5 保存在 word 中以來,s 的內(nèi)容已經(jīng)發(fā)生了變化。
word 中的索引與 s 中的數(shù)據(jù)不同步是危險的。幸運的是,Rust 有一個解決方案:字符串切片。
字符串切片是對字符串的一部分的引用,它看起來像這樣:
    let s = String::from("hello world");
    let hello = &s[0..5];
    let world = &s[6..11];
指定 [starting_index…ending_index],其中 starting_index 是片中的第一個位置,ending_index 比片中的最后一個位置大 1。

如果想從下標 0 開始,starting_index 可以省略,下面兩種切片是一樣的:
let s = String::from("hello");
let slice1 = &s[0..2];
let slice2 = &s[..2];
同樣,如果切片包含 String 的最后一個字符,ending_index 也可以省略,下面兩種切片是一樣的:
let s = String::from("hello");
let len = s.len();
let slice1 = &s[3..len];
let slice2 = &s[3..];
掌握了切片后,重寫之前的 first_word 函數(shù),這次返回的是字符串而非下標:
fn first_word(s: &String) -> &str {
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}
還記得之前程序中的錯誤嗎?當我們獲得了第一個單詞末尾的索引,但隨后清除了字符串,因此索引無效。這段代碼在邏輯上是不正確的,但沒有立即顯示出任何錯誤。如果我們一直嘗試將第一個單詞索引與空字符串一起使用,那么問題就會出現(xiàn)。切片使這個 bug 不可能出現(xiàn),并讓我們更快地知道我們的代碼有問題。
示例:
fn main() {
    let mut s = String::from("hello world");
    let word = first_word(&s);
    s.clear(); // error!
    println!("the first word is: {word}");
}
報錯:error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable。
回顧借用規(guī)則,如果有對某物的不可變引用,就不能同時使用可變引用。因為 clear 需要截斷 String,所以它需要獲得一個可變引用。println! 在調(diào)用 clear 之后使用 word 中的引用,因此不可變引用在那時必須仍然是活動的。Rust 不允許 clear 中的可變引用和 word 中的不可變引用同時存在,編譯失敗。
作為切片的字符串字面量
回想一下,我們討論過將字符串字面值存儲在二進制文件中?,F(xiàn)在我們知道了切片,我們可以正確地理解字符串字面值:
let s = "Hello, world!";
這里 s 的類型是 &str,一個指向二進制數(shù)據(jù)中特定點的切片。這也是為什么字符串字面值是不可變的,&str 是一個不可變引用。
作為參數(shù)的字符串切片
知道可以取字面量和字符串值的切片后,我們對 first_word 又做了一個改進,那就是它的聲明:
fn first_word(s: &str) -> &str {
定義一個函數(shù)來接受一個字符串切片而不是一個字符串的引用,將提高函數(shù)的靈活性。如果我們有一個字符串切片,我們可以直接傳遞。如果有一個 String 對象,則可以傳遞 String 對象的切片或?qū)?String 對象的引用。這種靈活性利用了取消強制轉(zhuǎn)換。
其他切片
數(shù)組也可以有切片:
let a = [1, 2, 3, 4, 5]; let slice = &a[1..3]; assert_eq!(slice, &[2, 3]);
這個切片的類型是 &[i32]。
總結(jié)
所有權(quán)、借用和切片的概念確保了 Rust 程序在編譯時的內(nèi)存安全。Rust 語言提供了對內(nèi)存使用的控制,但是當數(shù)據(jù)所有者超出范圍時,數(shù)據(jù)所有者會自動清理數(shù)據(jù),這意味著不必編寫和調(diào)試額外的代碼來獲得這種控制。
到此這篇關于深入理解Rust所有權(quán)的文章就介紹到這了,更多相關Rust所有權(quán)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
 在win10上使用mingw64編譯器配置Rust開發(fā)環(huán)境和idea 配置Rust 插件
在win10上配置 Rust 開發(fā)環(huán)境(使用 mingw64編譯器)和 idea 配置 Rust 插件的相關知識,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友參考下吧2023-03-03

