ปูสีส้มแดงต้นฉบับส่องข้อมูลในกล่องไม้ผ่านช่องมองโดยไม่ยกกล่องออกจากชั้น

Rust Option::as_deref: อ่าน String แบบ &str โดยไม่ย้ายเจ้าของ

พี่โชว์Engineering

config มักเก็บเป็น Option<String> เพราะต้องเป็นเจ้าของข้อความ แต่ฟังก์ชันตรวจค่าต้องการเพียง Option<&str> หากเขียน as_ref().map(String::as_str) ทุกครั้ง intent จะจมหายไปในพิธีการ Option::as_deref ทำสองขั้นนั้นให้ตรงความหมายและไม่ย้ายเจ้าของครับ

WHY: มองของในกล่องโดยไม่ยกกล่องไปทั้งใบ

String เหมือนกล่องที่ config เป็นเจ้าของ ส่วน &str เหมือนช่องมองที่ยืมเนื้อหาชั่วคราว as_deref เปลี่ยน &Option<String> ให้มองเป็น Option<&str> เราจึง trim, match หรือส่งเข้า validator ได้ โดยค่าเดิมยังอยู่และใช้ต่อหลัง borrow จบ

fn normalized_mode(value: &Option<String>) -> Option<&str> {
    value.as_deref().map(str::trim).filter(|s| !s.is_empty())
}

fn main() {
    let mode = Some(String::from("  safe  "));
    assert_eq!(normalized_mode(&mode), Some("safe"));
    assert_eq!(mode.as_ref().unwrap(), "  safe  ");

    let blank = Some(String::from("   "));
    assert_eq!(normalized_mode(&blank), None);
    assert_eq!(normalized_mode(&None), None);
    println!("borrowed config verified");
}

HOW: deref target อาจไม่ใช่ str

เมธอดนี้อาศัย trait Deref จึงใช้กับ Option<Box<T>>, Option<Rc<T>> และ smart pointer อื่นได้ด้วย ผลลัพธ์ยืมค่าอยู่ จึงห้ามคืน reference ที่อยู่นานกว่า owner และห้ามแก้ owner ระหว่างที่ borrow ยังถูกใช้ ถ้าต้องแก้ข้อความภายในให้ดู as_deref_mut แทน แต่ควรจำกัด scope ให้สั้น

อย่าใช้ clone() เพียงเพื่อผ่าน validator ที่รับ &str เพราะจะ allocate โดยไม่จำเป็น และอย่าตีความ filter ในตัวอย่างว่าเขียนค่ากลับ config มันแค่เปลี่ยนมุมมองผลลัพธ์ชั่วคราว ค่า String ต้นฉบับยังมีช่องว่างเหมือนเดิม

Recap

  • ใช้ as_deref เมื่อมี Option<String> แต่ต้องอ่านเป็น Option<&str>
  • ไม่ clone ค่าเพียงเพื่อส่งเข้า validator
  • จำว่าการ trim/filter ไม่แก้ owner ต้นฉบับ
  • รักษา lifetime ของ reference ให้อยู่ภายใน owner
  • ใช้ as_deref_mut เฉพาะเมื่อจำเป็นต้องแก้ผ่าน borrowed target