Rust core::range::Range: เก็บ span แบบ Copy โดยไม่ให้ range เป็น iterator
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