Rust程式語言內建的String結構體,可以用來儲存可變動的字串。在串接字串的時候,經常會使用到String結構實體的「push_str」方法,將其它字串存進該String結構實體現有的字串資料之後,其背後是使用Vec結構實體的「extend_from_slice」方法來處理的,效能還算不錯。但是如果我們要串接的字串只有一個字元的話,使用切片來處理如此小量的資料真的合適嗎?



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

let mut s = String::new();
s.push_str("/");

String結構實體的「push_str」方法,會去操作String結構實體內部的Vec結構實體,使用其「extend_from_slice」方法,將記憶體中已被UTF-8編碼的字串資料逐一複製到Vec結構實體的記憶體空間中。上面這個例子中,字串「/」的UTF-8編碼資料為「[47]」。試想一下,如果我們要將「47」放入一個「Vec<u8>」結構實體中會怎麼做呢?顯然我們會直接使用「Vec<u8>」結構實體的「push」方法來完成,而不會去將「47」轉成u8陣列「[47]」,然後再取得切片並用「extend_from_slice」方法來串接。後者的作法明顯比較耗時。

所幸,String結構實體除了「push_str」方法外,它還另外提供了一個「push」方法,可以直接傳入一個字元,將其串接到String結構實體現有的字串資料之後。以上程式可以改寫如下:

let mut s = String::new();
s.push('/');

這個「push」方法,會先利用字元的「len_utf8」方法,來計算該字元轉成UTF-8編碼的資料後,所需要用到的位元組數量。如果數量為1,就會直接操作內部的Vec結構實體,使用其「push」方法來串接資料;如果數量不為1,會把該字元用UTF-8編碼成u8陣列之後,再去使用Vec結構實體的「extend_from_slice」方法來串接。

也因此我們可以猜測出,當字元經過UTF-8編碼後的資料長度等於1時,String結構實體的「push」方法效能會比「push_str」方法好。但是當字元經過UTF-8編碼後的資料長度大於1時,「push_str」方法的效能就會比「push」方法好。

實測看看?

為了驗證我們的猜測,就要實際寫一段程式來測試運算效能啦!這段程式可以在GitHub上取得:

根據測試結果我們可以發現,當傳入「push」方法的字元經過UTF-8編碼後的資料長度等於1時,其效率可以達到「push_str」方法的兩倍以上。但是當字元經過UTF-8編碼後的資料長度大於1時,「push_str」方法的速度並沒有減慢太多,反倒是「push」方法比「push_str」方法還要更慢了。

所以,當串接的字串長度只有1時,如果其UTF-8編碼的資料長度等於1,就使用String結構實體的「push」方法以得到更好的效能,否則就使用「push_str」方法吧!