通过获取可变引用可以移动值吗?

4

Playpen

pub trait Skip<I: Iterator> {
    fn skip(&mut self, steps: usize);
}

impl<I: Iterator> Skip<I> for I {
    fn skip(&mut self, mut steps: usize) {
        for _ in self {
            steps -= 1;
            if steps <= 0 {
                break;
            }
        }
    }
}

fn main() {
    let s  = "abc123def";
    let mut chars = s.chars();
    chars.skip(2);
    println!("{:?}", chars.collect::<String>());
}

错误:

error: use of moved value: `chars` [--explain E0382]
  --> <anon>:20:22
19 |>     chars.skip(2);
   |>     ----- value moved here
20 |>     println!("{:?}", chars.collect::<String>());
   |>                      ^^^^^ value used here after move
<anon>:20:22: 20:27: note: in this expansion of format_args!
<anon>:20:22: 20:27: note: in this expansion of print! (defined in <std macros>)
<anon>:20:22: 20:27: note: in this expansion of println! (defined in <std macros>)
note: move occurs because `chars` has type `std::str::Chars<'_>`, which does not implement the `Copy` trait

error: aborting due to previous error
playpen: application terminated with error code 101

答案解释了为什么这不起作用以及如何解决它,但是老实说,即使它有稍微不同的 API(它是一个迭代器适配器而不是一个变异方法),最好还是只使用 stdlib 的 skip - user395760
经过一些对可变迭代器的实验,我同意 @delnan 的观点! - Sandeep Datta
1个回答

5

可通过获取可变引用来移动值吗?

不可以。引用不会移动所指向的项。这是引用的一个重要特点。

然而,Iterator::skip会获取迭代器的所有权,即发生了移动操作:

fn skip(self, n: usize) -> Skip<Self>

为了解决这个错误,你可以...获取一个可变引用!
chars.by_ref().skip(2);

然而,请注意,调用迭代器适配器却没有使用它什么也不做

警告:必须使用未使用的结果:迭代器适配器是懒惰的,除非被消耗,否则什么也不做。

< p >真正的问题在于你并没有调用你认为的 skip 方法。如果你将你的方法重命名为 skip2,你会发现它按预期工作。< /p > < p >这是因为方法查找优先考虑按值(self)接受接收者的方法,其次是按引用(&self),最后是按可变引用(&mut self)接受接收者的方法。 < p >为迭代器选择与标准库相同的方法名称可能是个坏主意,尤其是对于迭代器。但是,令人惊讶的是,没有打印警告,说明有多个适用的方法在范围内。 < p >不过,您可以使用通用函数调用语法(UFCS)来指定要调用的实现方法:
pub trait Skip: Iterator {
    fn skip(&mut self, steps: usize) {
        for _ in self.take(steps) {}
    }
}

impl<I> Skip for I where I: Iterator {}

fn main() {
    let s  = "abc123def";
    let mut chars = s.chars();
    Skip::skip(&mut chars, 2);
    println!("{:?}", chars.collect::<String>());
}

1
如果直接将Skip::skip更改为接受I,或者在可变引用上调用它(例如(&mut chars).skip(2)),则会出现错误。否则是合法的原因是Iterator的方法不需要自动引用,因此更具体且更受欢迎。rustc中的lint非常简单,但是clippy可能有一个lint来处理这个问题。 - user395760
@delnan 我想知道选择方法的顺序(self 然后 &self 然后 &mut self),并且“多个适用方法”错误只会在同一类中有多个方法时发生? - Shepmaster
@Shepmaster 我认为它是以是否需要自动引用为基础来表述的,但基本上是的。 - user395760

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接