Rust 技巧:AsRef-str- 和 Into-String-

Rustd 的字符串類型有兩種不同變體:&str 和 String。它們的不同之處在於 & str 是對 String 或 str 的引用,String 是動態分配的字符串對象。

use std::time::{SystemTime, UNIX_EPOCH};

fn time_stamp(msg: &str) -> String {
    let time = SystemTime::now()
        .duration_since(UNIX_EPOCH).unwrap().as_millis();
    time.to_string() + ": " + msg
}

fn main() {
    let s1 = "msg as &str";
    let s2 = String::from("msg as String");
    println!("{}", time_stamp(s1));
    println!("{}", time_stamp(&s2)); // 注意額外的' & '字符
}

由於函數 time_stamp() 接受 & str 類型作爲形參,因此必須顯式地將 String 類型轉換爲帶有 & 字符的字符串,否則,我們將得到 Rust 編譯錯誤。如果 Rust 可以自動處理這兩種類型豈不是很好?這是可以的!不需要指定具體的參數類型 & str,我們可以指定一個實現 AsRef trait 的類型:

fn time_stamp(msg: impl AsRef<str>) -> String {
    let time = SystemTime::now()
        .duration_since(UNIX_EPOCH).unwrap().as_millis();
    time.to_string() + ": " + msg.as_ref()
}

fn main() {
    let s1 = "msg as &str";
    let s2 = String::from("msg as String");
    println!("{}", time_stamp(s1));
    println!("{}", time_stamp(s2)); // 去掉了' & '字符
}

現在,我們可以將任何實現 AsRef 的類型傳遞給函數,比如 & str 或 String。這在爲庫設計 API 時特別有用,因爲它使客戶端代碼更容易編寫。

不過要注意,也不是在任何情況下都使用這個技巧。考慮下面的函數,你覺得有什麼問題嗎?

fn concat(msg1: impl AsRef<str>, msg2: impl AsRef<str>) -> String {
    msg1.as_ref().to_owned() + "; " + msg2.as_ref()
}

當爲 msg1 提供的類型已經是一個 String 時,函數 concat 正在做不必要的工作,因爲它正在調用 msg1 上的 to_owned() 方法。理想情況下,如果 msg1 已經是 String 類型,我們希望重用它。爲此,你可以使用另一個 Into 的 trait:

fn concat(msg1: impl Into<String>, msg2: impl AsRef<str>) -> String {
    msg1.into() + "; " + msg2.as_ref()
}

總之,如果首選類型是 & str,使用 AsRef trait 綁定,如果首選類型是 String,使用 Into trait 綁定。此外,只要注意不要像前面的例子那樣執行不必要的工作,就不會有性能損失!

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/W2LTSUVV12ETFDlJenv0ag