Rust程式語言融合了多種程式設計法(programming paradigm),其中的指令式程式設計(imperative programming)所用的迴圈和函數式程式設計(functional programming)所提供的迭代器(iterator)可以加強陣列或是切片的走訪效能。然而,很多時候我們並不需要直接走訪到陣列或是切片的最尾端,而是只想要在經過指定的迭代次數之後,就結束走訪,這時可以利用Rust提供的陣列轉切片或是切片再切片的功能來進行索引值範圍的限縮,事實上迭代器也有提供take方法,可以在執行完指定的迭代次數之後,提前終止後續待走訪的元素。那如果迭代器的走訪對象是陣列或是切片時,使用take方法會比直接使用更小範圍的切片還要慢嗎?



我們先來看看以下程式碼:

let array = [2, 2, 3, 4, 5, 6, 7, 8];

for i in 0..5 {
    println!("{}", array[i]);
}

以上程式,會利用For計次迴圈來從頭開始走訪這個存在於堆疊內的陣列,並將走訪到的元素值印在螢幕上,直到走訪到索引4的元素為止。

先前的文章中,我們已經知道用For迴圈來走訪堆疊內的陣列,效能跟和用For迭代器迴圈或是迭代器是一樣的。

所以,以上程式其實也會等效於以下的For迭代器迴圈程式:

let array = [2, 2, 3, 4, 5, 6, 7, 8];

for &n in array[..5].iter() {
    println!("{}", n);
}

以上程式,利用Rust提供的陣列轉切片功能,切片範圍只有陣列前5個元素,所以走訪這個切片,就相當於從頭開始走訪原陣列,直到索引4的元素為止。那如果我們不使用切片,而是用迭代器提供的take方法來完成這個在走訪陣列時指定迭代次數的功能呢?程式如下:

let array = [2, 2, 3, 4, 5, 6, 7, 8];

for &n in array.take(5) {
    println!("{}", n);
}

使用take方法所二度產生出來的迭代器,會影響到效能嗎?

效能實測

直接實際寫一段程式來測試運算效能吧!這段程式可以在GitHub上取得:

https://github.com/magiclen/rust-performance-measurement/blob/master/benches/iter_take.rs

根據測試結果,可以發現使用take方法的效能與使用切片是幾乎一樣的。這是因為take方法所產生的Take結構實體,只不過就是多加了一個變數來記錄其剩餘的next方法的呼叫次數。程式如下:

fn next(&mut self) -> Option<<I as Iterator>::Item> {
    if self.n != 0 {
        self.n -= 1;
        self.iter.next()
    } else {
        None
    }
}

所以它對程式效能影響不大。