ปูสีส้มแดงต้นฉบับกำลังคัดลอกกรอบวัดช่วงเดียวกันไปวางบนริบบิ้นสองเส้น

Rust core::range::Range: เก็บ span แบบ Copy โดยไม่ให้ range เป็น iterator

พี่โชว์Engineering

span ของ parser หรือ editor เป็นเพียงพิกัดเริ่ม–จบ แต่ std::ops::Range แบบเดิมทำหน้าที่เป็น iterator ด้วย จึงไม่เป็น Copy เพื่อไม่ให้การ copy แอบทำซ้ำสถานะการวนครับ Rust 1.96 เพิ่ม range ชุดใหม่ใน core::range ซึ่งเป็นข้อมูลช่วงและใช้ IntoIterator แยกต่างหาก ทำให้เก็บใน struct ที่เป็น Copy ได้

ภาพง่าย ๆ คือไม้บรรทัดกับนิ้วที่กำลังไล่ตามขีด ไม้บรรทัดควรถ่ายสำเนาได้โดยไม่เปลี่ยนตำแหน่งนิ้ว การแยก range ออกจาก iterator ทำให้สองบทบาทนี้ไม่พันกัน

ตัวอย่างโค้ดบางส่วนด้านล่างเป็น illustrative fragment / pseudocode สำหรับ API ใหม่ — toolchain ตรวจอาจยังไม่เปิด new_range_api จึงไม่รันเทสต์อัตโนมัติ

use core::range::Range;

#[derive(Clone, Copy)]
struct Span(Range<usize>);

impl Span {
    fn slice(self, source: &str) -> &str {
        &source[self.0]
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn copied_span_can_be_reused() {
        let span = Span(Range { start: 5, end: 9 });
        let same = span;
        assert_eq!(span.slice("mint cache"), "cach");
        assert_eq!(same.slice("warm cache"), "cach");
    }
}

Syntax ยังไม่ได้เปลี่ยนทั้งหมด

ณ Rust 1.96 syntax 0..4 ยังสร้าง legacy range ใน core::ops ดังนั้นตัวอย่างจึงสร้าง core::range::Range { start, end } ชัด ๆ เอกสาร release ระบุว่า syntax จะย้ายในอนาคต ทีม library ควรรับ impl RangeBounds เมื่ออยากรองรับทั้งชนิดเดิมและใหม่ แทนการล็อก concrete type โดยไม่จำเป็น

range ของ string ยังเป็น byte offsets ต้องอยู่บนขอบ UTF-8 มิฉะนั้น indexing จะ panic และ span ที่เก็บไว้ผูกกับ snapshot ของข้อความเดิม อย่าแก้ source แล้วใช้พิกัดเก่าโดยไม่มี revision check ส่วนงานที่ต้อง consume iterator จริง สามารถเรียก into_iter() แล้วเก็บ iterator แยกจากข้อมูลช่วง

Recap

  • ใช้ core::range::Range เมื่อ span เป็นข้อมูลที่อยากให้ Copy
  • จำไว้ว่า a..b ยังเป็น legacy range ในรุ่นนี้
  • เปิด public API ด้วย RangeBounds หากต้องรับได้ทั้งสองตระกูล
  • ผูก byte span กับ source revision และตรวจขอบ UTF-8