我有一个结构,通过方法next
的方法Iterator
给出数字:
struct Numbers{
number: usize,
count: usize
}
impl Iterator for Numbers {
type Item = usize;
fn next(&mut self) -> Option<Self::Item> {
if self.count > 0 {
self.count -= 1;
return Some(self.number);
}
return None;
}
}
fn main(){
let numbers = Numbers{
number: 777,
count: 10
};
for n in numbers {
println!{"{:?}", n};
}
}
它与usize
类型正常工作。
但是Box类型的相同代码会产生编译错误:
struct Numbers{
number: Box<usize>,
count: usize
}
impl Iterator for Numbers {
type Item = Box<usize>;
fn next(&mut self) -> Option<Self::Item> {
if self.count > 0 {
self.count -= 1;
return Some(self.number);
}
return None;
}
}
fn main(){
let numbers = Numbers{
number: Box::new(777),
count: 10
};
for n in numbers {
println!{"{:?}", n};
}
}
./ numbers.rs:12:25:12:29错误:无法摆脱借来的内容
./ numbers.rs:12 return some(self.number);
如何正确实现盒装值的迭代器?
答案 0 :(得分:6)
这归结为Rust的所有权模型以及复制和移动语义之间的区别; Box<T>
已移动语义,未实施Copy
,因此return Some(self.number);
会移动self.number
,取得其所有权;但这是不允许的,因为它需要消耗self
,这只能通过可变参考来实现。
你有几个选择(在那里我写“带有移动语义的对象”,我的意思是在这个特定的情况下self.number
):
不要使用移动语义返回对象,使用复制语义引用返回其他内容,例如引用而不是装箱值(返回引用将要求迭代器对象与要迭代的对象不同结束,以便您可以在Item
中写下生命周期;因此它不适用于您的特定用例)或未装箱的数字。
构造一个基于具有移动语义的对象返回的新值:
impl Iterator for Numbers {
type Item = Box<usize>;
fn next(&mut self) -> Option<Self::Item> {
if self.count > 0 {
self.count -= 1;
Some(Box::new(self.number))
} else {
None
}
}
}
使用移动语义克隆对象(这是第二个选项的简化形式,真的):
impl Iterator for Numbers {
type Item = Box<usize>;
fn next(&mut self) -> Option<Self::Item> {
if self.count > 0 {
self.count -= 1;
Some(self.number.clone())
} else {
None
}
}
}
构造一个新值来代替具有移动语义的对象:
use std::mem;
impl Iterator for Numbers {
type Item = Box<usize>;
fn next(&mut self) -> Option<Self::Item> {
if self.count > 0 {
self.count -= 1;
let number = mem::replace(&mut self.number, Box::new(0));
// self.number now contains 0
Some(number)
} else {
None
}
}
}