我正在學習 Rust 并且正在測驗通過函式進行的一些陣列復制。我確信有內置的 Rust 函式來復制/克隆陣列資訊,但是我認為個人實作是一個好主意,可以幫助我理解通過函式傳遞參考。
fn copy_str_arr_original (a1: [&str; 60], a2: &mut [&str; 60]) {
// copy 1 into 2
for i in 0..60 {
a2[i] = a1[i];
} // change is reflected in a2 as it is passed as &mut
}
但是,這引發these two types are declared with different lifetimes...了&str型別本身的錯誤。經過一些進一步的學習,我嘗試宣告自己的一生并將它們分配給它,然后就解決了!
fn copy_str_arr_fix<'a> (a1: [&'a str; 60], a2: &mut [&'a str; 60]) {
// copy 1 into 2
for i in 0..60 {
a2[i] = a1[i];
} // change is reflected in a2 as it is passed as &mut
}
但是,為什么會這樣呢?為什么陣列中的值型別需要分配生命周期而不是引數本身?換句話說,為什么這根本不起作用?
fn copy_str_arr_bad<'a> (a1: &'a [&str; 60], a2: &'a mut [&str; 60]) {
// does not work... ^-----------------------^-------- different lifetimes
for i in 0..60 {
a2[i] = a1[i];
}
}
我仍在努力了解生命周期在更復雜的物件(如陣列和結構)的背景關系中是如何作業的,因此將不勝感激任何解釋!
uj5u.com熱心網友回復:
錯誤訊息有點令人困惑,因為它指的是根據生命周期省略規則生成的生命周期。在您的情況下,終身省略意味著:
fn copy_str_arr_original(a1: [&str; 60], a2: &mut [&str; 60])
是語法糖:
fn copy_str_arr_original<'a1, 'a2_mut, 'a2>(a1: [&'a1 str; 60], a2: &'a2_mut mut [&'a2 str; 60])
換句話說,我們有三個完全不相關的生命周期。“不相關”意味著呼叫者可以選擇與他們相關聯的物件存活多長時間。例如,in 中的字串a2可能是靜態的并且一直存在到程式結束,而 in 中的字串a1可能會在copy_str_arr_original()回傳后立即洗掉。或者反過來。如果這種自由度看起來可能會導致問題,那么您就在正確的軌道上,因為借用檢查員同意您的意見。
請注意,有點違反直覺,'a2_mut生命周期的長度完全無關,它可以根據呼叫者的喜好而長或短。我們的函式已經收到參考,因此可以在函式的作用域內使用它。'a2_mutLifetime 告訴我們它將在函式范圍之外存活多久,我們只是不在乎這一點。
'a1并且'a2是另一回事。由于我們打算以復制參考a1到a2,我們正在有效地鑄造內參考a1(型別&'a1 str)存盤在參考型別a2(這是&'a2 str):
a2[i] = a1[i]; // implicitly casts &'a1 str to &'a2 str
對于要有效,&'a1 str必須是一個亞型的&'a2 str。雖然 Rust 沒有 C 意義上的類和子類,但它確實有涉及生命周期的子型別。從這個意義上說,如果 A 值的值保證至少與 B 的值一樣長,則 A 是 B 的子型別。換句話說,'a1必須至少離開 ,'a2表示為'a1: a2。所以這編譯:
fn copy_str_arr<'a1: 'a2, 'a2, 'a2_mut>(a1: [&'a1 str; 60], a2: &'a2_mut mut [&'a2 str; 60]) {
for i in 0..60 {
a2[i] = a1[i];
}
}
強制轉換成功的另一種方法是只要求生命周期相同,您在代碼中有效地做到了這一點。(您還省略了'a2_mut生命周期,編譯器正確地將其解釋為對無關匿名生命周期的請求。)
uj5u.com熱心網友回復:
假設您可以定義copy_str_arr兩個不同的、不相關的生命周期,如下所示:
fn copy_str_arr<'a, 'b>(a1: [&'a str; 60], a2: &mut [&'b str; 60]) {
// ...
}
然后考慮這個例子:
let mut outer: [&str; 60] = [""; 60];
{
let temp_string = String::from("temporary string");
let inner: [&str; 60] = [&temp_string; 60];
// this compiles because our bad `copy_str_arr` function allows
// `inner` and `outer` to have unrelated lifetimes
copy_str_array(&inner, &mut outer);
} // <-- `temp_string` destroyed here
// now `outer` contains references to `temp_string` here, which is invalid
// because it has already been destroyed!
println!("{:?}", outer); // undefined behavior! may print garbage, crash your
// program, make your computer catch fire or anything else
如您所見,如果a1和a2被允許具有完全不相關的生命周期,那么我們最終可能會遇到這樣一種情況,其中一個陣列包含對無效資料的參考,這是非常糟糕的。
但是,壽命不必相同。您可以改為要求您正在復制的生命周期比您復制到的生命周期更長(從而確保您不會非法延長參考的生命周期):
fn copy_str_arr<'a, 'b>(a1: &[&'a str; 60], a2: &mut [&'b str; 60])
where
'a: 'b, // 'a (source) outlives 'b (destination)
{
for i in 0..60 {
a2[i] = a1[i];
}
}
uj5u.com熱心網友回復:
簡單的答案是編譯器不是很聰明。
您不必在每次定義處理參考的函式時都指定一堆生命周期,這只是因為編譯器在可能的情況下進行了一些有根據的猜測。所以它有點聰明,但不是很聰明。
假設您正在撰寫一個函式,該函式接受對結構的參考并回傳對該結構中欄位的參考:
struct Book {
pages: u16,
title: String,
}
fn borrow_title(book: &Book) -> &str {
&book.title
}
十分之九它確實是對您傳遞的引數的參考。但有時不是:
fn borrow_title(book: &Book) -> &'static str {
if book.pages > 10 {
"Too long..."
} else {
"Not long enough"
}
}
如您所見,您需要指定回傳&str的生命周期不同(在這種情況下,特殊的'static.
因此,既然您這么說fn copy_str_arr_original (a1: [&str; 60], a2: &mut [&str; 60]),編譯器實際上并未對您的實作進行推理,也不知道 in 中參考的生命周期a1應該至少與任何參考中的生命周期一樣長a2。
至于第二部分,您需要考慮參考只是指向某些資料的指標。該資料可以包含其他參考。在這種情況下,重要的是這些其他參考。
這里有 2 個字串參考陣列。假設您將參考從第一個復制到第二個。是否通過參考將這些陣列傳遞給函式并不重要。重要的是,如果第一個陣列的所有權被洗掉,字串也會被洗掉。如果第二個陣列仍然持有任何參考,這將導致不安全的記憶體處理。
為簡化起見,讓我們考慮只有一個字串,我們要將值借入一個陣列,然后將這些借用值復制到另一個陣列,洗掉第一個陣列,然后洗掉該字串。你希望發生什么?
編譯器將拋出一個合適的問題,以確保沒有對字串的參考。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/347355.html
上一篇:選擇兩個陣列中的值
