为何iter+filter后collect返回Vec<&Shoe>?如何获取Shoe类型实例?
Rust代码示例
#[derive(Debug)] struct Shoe { size: u32, style: String, } fn main() { let shoes = vec![ Shoe { size: 10, style: String::from("sneaker"), }, Shoe { size: 13, style: String::from("sandal"), }, Shoe { size: 10, style: String::from("boot"), }, ]; let shoe_size = 10; let filtered_shoes: Vec<&Shoe> = shoes.iter().filter(|s| s.size == shoe_size).collect(); let shoe_0: &Shoe = shoes.get(0).unwrap(); println!("address vec shoe {:p}", shoes.as_ptr()); println!("address filtered shoe {:p}", filtered_shoes.as_ptr()); println!("{:?}", filtered_shoes); println!("{:?}", shoe_0); }
(注:补充了Shoe的Debug推导,修正了地址打印的方式,用as_ptr()更准确反映元素起始地址)
问题解答
为什么
shoes.iter().filter(...).collect()返回Vec<&Shoe>而非Vec<Shoe>?
因为iter()返回的是不可变引用迭代器,它不会夺取原Vec中元素的所有权,只是遍历每个元素的引用(&Shoe)。filter方法只会筛选符合条件的元素,不会改变元素的类型,所以筛选后迭代器的元素还是&Shoe,最终collect就把这些引用收集成了Vec<&Shoe>。这种设计是为了在不破坏原Vec所有权的前提下操作元素,避免不必要的内存复制,同时保证原Vec还能继续使用。对比JavaScript的
filter复制原元素返回新数组,这里两个向量地址不同但元素是引用类型的原因是什么?
JS的filter行为分两种:对于数字、字符串这类原始值,会直接复制值到新数组;对于对象则是复制对象的引用到新数组。而Rust的设计核心是所有权和零成本抽象:
- 两个Vec地址不同,是因为
collect会创建一个全新的Vec实例来存筛选结果,容器本身是独立的,内存地址自然不一样。 - 元素是引用类型,是因为我们用
iter()迭代的是原Vec元素的引用,filter只是挑出符合条件的引用,新Vec里存的是指向原Vec中Shoe实例的指针,而非复制整个Shoe对象。这种方式避免了复制大型对象的开销,同时严格遵守所有权规则——原Vec仍然拥有这些Shoe的所有权,新Vec只持有引用。
- 如何让
shoe_0的类型变为Shoe而非&Shoe?
根据是否需要保留原Vec中的元素,有几种常用方式:
- 不需要保留原Vec时:用
remove(0)方法,它会移除索引0处的元素并返回,直接转移所有权:let mut shoes = vec![/* ... */]; let shoe_0: Shoe = shoes.remove(0); - 需要保留原Vec时:给
Shoe实现Clonetrait,然后调用clone()复制元素:#[derive(Debug, Clone)] struct Shoe { /* ... */ } let shoes = vec![/* ... */]; let shoe_0: Shoe = shoes[0].clone(); - 消耗原Vec获取元素:用
into_iter()迭代器,它会夺取原Vec的所有权,迭代的是Shoe类型本身,再通过next()取元素:let mut shoes_iter = shoes.into_iter(); let shoe_0: Shoe = shoes_iter.next().unwrap();
内容的提问来源于stack exchange,提问作者vito
相关产品推荐
相关产品推荐

