切片(slice,类型[T]和&[T])在Rust中被引入,用于安全而高效地访问数组、向量和其他元素序列的子集。它们避免了分配和重复复制数据,仅提供对集合部分的“视图”或窗口。这与在编译时固定大小的数组及存储指针和长度但拥有内存的动态集合是不同的。
在没有严格的生命周期控制的语言中,处理数组和向量时,常常会出现越界错误、内存泄漏和使用无效指针的问题。重要的是,在处理集合的子集时,必须避免复制并确保内存安全,这对于系统级别尤其重要。
在Rust中,切片是对数据部分的“指针 + 长度”,不拥有内容。它们总是伴随生命周期,编译器保证切片不会比原始数据(数组、Vec、String)存活得更长。所有对切片的操作都通过安全的访问方法进行,任何越界访问都会导致运行时的panic。
代码示例:
let arr = [1, 2, 3, 4, 5]; let slice = &arr[1..4]; // [2,3,4] 类型: &[i32] let mut vec = vec![10, 20, 30]; let mut_slice: &mut [i32] = &mut vec[..2]; mut_slice[0] = 99; assert_eq!(vec, [99, 20, 30]);
关键特性:
可以创建超出原数组或向量大小的切片吗?
不可以。编译器和运行时保证切片只能在原始数据的有效索引中创建。试图越界会导致panic.
let arr = [1, 2, 3]; let s = &arr[0..4]; // 在运行时panic
切片是内存的独立拥有者吗?
不是。切片只是数据的“窗口”,它们不拥有内存。如果尝试从函数中返回切片,但源是局部的,会导致编译时错误.
fn give_slice() -> &[i32] { let arr = [1,2,3]; &arr[1..] } // 错误:arr的生命周期不足
切片与Rust中的数组在类型和操作上有什么区别?
数组具有在编译时已知的固定长度,并完全嵌入在栈中。切片可以是任意长度,动态确定,并始终存储指针和长度.
let a: [u32; 3] = [1,2,3]; // 固定长度数组 let s: &[u32] = &a[..]; // 任意大小的切片
程序员从一个函数中返回了切片,该函数中创建了局部数组。函数返回后,原始数组被删除,切片变成了“悬空”指针。这导致了错误,甚至崩溃。
优点:
缺点:
切片始终通过引用外部数据创建,数据的拥有者和切片的生命周期是相同的。编译器保证切片与源之间有严格的生命周期关系。
优点:
缺点: