Rust 新手程式設計師/引用和借用的基礎
假設我們有一個整數向量,我們想要找到它的眾數(出現次數最多的數字)。最大的問題是我們必須儲存每個數字出現的次數,這樣我們才能確保最終得到最常見的數字。我們想要的是一個對映。對映允許我們從鍵到值進行對映。在本例中,鍵是數字,值是它出現的次數。這裡,我們將使用一個數組作為基本的對映。陣列有點像對映,因為它使用索引作為鍵,而陣列中的值是值。以下是函式
fn compute_mode(input: Vec<i32>) -> i32 {
//compute count of numbers
let mut number_count = [0; 100];
for index in 0..input.len() {
let number = input[index];
number_count[number as usize] += 1;
}
//work out which one is the highest
let mut highest_count = 0;
let mut highest_index = 0;
for index in 0..number_count.len() {
let count = number_count[index];
if count > highest_count {
highest_count = count;
highest_index = index;
}
}
return highest_index as i32;
}
這裡有很多有趣的東西需要討論。首先,[0; 100] 語法表示我們將陣列初始化為 100 個 0。這是一個非常方便的簡寫。接下來,什麼是 usize?本質上,不同的計算機使用不同的位數來引用陣列索引。為了反映這一點,我們可以使用 usize 作為一種資料型別,它符合使用者使用的任何計算機型別。它是無符號的,因為我們不能有負索引。所有陣列的索引都使用 usize,來自 'for index in 0..input.len()' 的索引也是 usize,因為 input.len() 返回一個 usize。還有一個相應的 isize 資料型別,但它使用得少得多,它最適合於索引偏移,在這些偏移中您可能會有負偏移。
使用陣列作為對映有一些明顯的缺點
- 輸入的數字必須為正數,因為用於索引它們的 usize 是正數。
- 輸入的數字必須適合陣列的大小。這裡我們設定陣列的大小為 100,您可以將其設定得更高,但這將佔用更多記憶體
- 可能會有很多記憶體浪費,因為對於我們使用的向量,大多數數字將保持為 0,但仍然會佔用空間。
這個問題有解決方法,但現在瞭解這些缺點就足夠了,而且對於我們現在正在做的事情來說已經足夠了。
現在我們使用這個函式,我們會得到
fn main() {
let numbers = vec![3, 5, 6, 1, 3, 6, 3, 5];
let mode = compute_mode(numbers);
println!("{}", mode);
}
我們得到 3,這正是我們預期的。假設我們還想要打印出向量的第 4 個值(索引為 3)
fn main() {
let numbers = vec![3, 5, 6, 1, 3, 6, 3, 5];
let mode = compute_mode(numbers);
println!("{}", mode);
println!("{}", numbers[3]);
}
但是,如果我們嘗試執行它,現在我們會得到這個錯誤?“對已移動值的借用:`numbers`”這是什麼意思?
這是 Rust 的基本概念之一,即所有權和借用的概念。本質上,一個變數一次只能在一個地方擁有。在這個例子中,透過呼叫函式 compute_mode(numbers),我們將所有權傳遞給該函式,並且之後就無法再使用該變量了。這是一個非常重要的概念,需要理解。
但是,最好的解決方案是什麼?每個函式都需要擁有一個變數才能使用它嗎?答案是否定的,答案在於借用。這與您可能期望的借用是一樣的,該函數借用該變數並在完成使用後將其歸還。借用是使用所謂的引用來完成的。引用是普通型別的附加項,在開頭帶有一個“&”符號。例如,&i32 是對 i32 整數的引用。如果您有一個變數的引用,則您不能更改/修改該變數,除非它是可變引用。可變引用將寫為 &mut i32,它將提供對 i32 整數的可變引用。
對於我們的 compute_mode 函式,我們不需要修改向量,因此我們只需要將其更改為
fn compute_mode(input: &Vec<i32>) -> i32 {
//the inside of the function remains the same
}
然後在我們的 main 函式中,我們做
fn main() {
let numbers = vec![3, 5, 6, 1, 3, 6, 3, 5];
let mode = compute_mode(&numbers);
println!("{}", mode);
println!("{}", numbers[3]);
}
請注意,我們在呼叫函式時必須指定我們正在獲取對 numbers 的引用。現在,這將執行沒有任何錯誤!