我想把下面的Padded类型变成一个迭代器Transformer:

enum Step<T> {
    Before(T),
    During(T),
    After
}

struct Padded<T> {
    step: Step<T>
}

(请注意,在我的实际代码中,Padded中除了Step之外还有其他东西,因此从structenum有额外的间接层.)

我们的 idea 是,在每次迭代中,我们总是更改我们的Step,因此将存储在其中的T移到下一个Step‘S构造函数中应该是合理的.然而,我不能让它工作.

简单的版本:

impl<T: Iterator> Iterator for Padded<T> {
    type Item = Option<T::Item>;

    fn next(&mut self) -> Option<Self::Item> {
        match &self.step {
            Step::Before(start) => {
                self.step = Step::During(*start);
                Some(None)
            },
            Step::During(ref mut iter) => {
                match iter.next() {
                    Some(x) => { self.step = Step::During(*iter); Some(Some(x)) },
                    None => { self.step = Step::After; Some(None) },
                }
            },
            Step::After => {
                None
            }
        }
    }
}

由于以下原因,此操作失败:

error[E0507]: cannot move out of `*start` which is behind a shared reference
  --> src/lcd.rs:39:42
   |
39 |                 self.step = Step::During(*start);
   |                                          ^^^^^^ move occurs because `*start` has type `T`, which does not implement the `Copy` trait

error[E0596]: cannot borrow data in a `&` reference as mutable
  --> src/lcd.rs:42:26
   |
42 |             Step::During(ref mut iter) => {
   |                          ^^^^^^^^^^^^ cannot borrow as mutable

error[E0507]: cannot move out of `*iter` which is behind a mutable reference
  --> src/lcd.rs:44:59
   |
44 |                     Some(x) => { self.step = Step::During(*iter); Some(Some(x)) },
   |                                                           ^^^^^ move occurs because `*iter` has type `T`, which does not implement the `Copy` trait

我试着通过将第三个分支改为:

            Step::After => {
                self.step = Step::After;
                None
            }

但这并不能改变任何事情.

然后我想我应该更清楚地说明这里发生了什么:

impl<T: Iterator> Padded<T> {
    fn next_self_and_item(self) -> (Self, Option<Option<T::Item>>) {
        match self.step {
            Step::Before(start) => {
                (Padded{ step: Step::During(start) }, Some(None))
            },
            Step::During(mut iter) => {
                match iter.next() {
                    Some(x) => (Padded{ step: Step::During(iter) }, Some(Some(x))),
                    None => (Padded{ step: Step::After }, Some(None)),
                }
            },
            Step::After => {
                (Padded{ step: Step::After }, None)
            }
        }
    }
}

此函数确实通过了borrow 判断器,但不能用于实现Iterator::next:

impl<T: Iterator> Iterator for Padded<T> {
    type Item = Option<T::Item>;

    fn next(&mut self) -> Option<Self::Item> {
        let (new_self, item) = self.next_self_and_item();
        *self = new_self;
        item
    }
}
error[E0507]: cannot move out of `*self` which is behind a mutable reference
  --> src/lcd.rs:79:32
   |
79 |         let (new_self, item) = self.next_self_and_item();
   |                                ^^^^ -------------------- `*self` moved due to this method call
   |                                |
   |                                move occurs because `*self` has type `Padded<T>`, which does not implement the `Copy` trait
   |
note: `Padded::<T>::next_self_and_item` takes ownership of the receiver `self`, which moves `*self`
  --> src/lcd.rs:57:27
   |
57 |     fn next_self_and_item(self) -> (Self, Option<Option<T::Item>>) {
   |                           ^^^^

那么我能做些什么呢?另外,我认为至少在道德上,我试图做的应该是好的,因为self是一个可变的引用(所以我应该能够做我想做的任何事情),并且在所有分支中,self.step内部的T只是被移动?

推荐答案

正如 comments 中指出的,not允许将非Copy的值从引用中移出,而不关心那里保留的是什么.这是因为Rust引用归其他人所有,并且应该始终引用有效数据.如果编译器允许这样的提取,那么死机(或者只是提前返回)会给所有者留下一个被 destruct 的值,从而导致崩溃或更糟.

但是,如果您可以提供有效的备用值,则std::mem::replace()允许您从引用中提取该值,同时将指定的替换保留在其位置.因为您的Step枚举有一个不包含T的变体,所以您可以通过(临时)用Step::After替换self.step来轻松地检索您的"烫手山芋":

fn next(&mut self) -> Option<Self::Item> {
    // take ownership of self.step by replacing it with Step::After
    match std::mem::replace(&mut self.step, Step::After) {
        Step::Before(start) => {
            self.step = Step::During(start);
            Some(None)
        }
        Step::During(mut iter) => match iter.next() {
            Some(x) => {
                self.step = Step::During(iter);
                Some(Some(x))
            }
            None => Some(None), // we've already changed it to Step::After
        },
        Step::After => None,
    }
}

Playground

允许std::mem::replace()将值从mut引用中移出,因为它保证在换入新值之前不会运行任意代码(可能会死机).

Rust相关问答推荐

通用池类型xsx

如何从polars DataFrame中获取一个列作为Option String?<>

带扫描的铁 rust 使用滤镜

限制未使用的泛型导致编译错误

有没有办法指定只在Rust的测试中有效的断言?

当发送方分配给静态时,Tokio MPSC关闭通道

变量需要parse()中的显式类型

用于判断整数块是否连续的SIMD算法.

我无法理解Rust范围的定义(Rust Programming Language,第二版克拉布尼克和尼科尔斯)

使用关联类型重写时特征的实现冲突

Rust 并行获取对 ndarray 的每个元素的可变引用

仅在使用 &mut 或线程时borrow 的数据在闭包之外转义?

当在lambda中通过引用传递时,为什么会出现终身/类型不匹配错误?

Rust 中的 Option as_ref 和 as_deref 有什么不同

如何递归传递闭包作为参数?

为什么在 macOS / iOS 上切换 WiFi 网络时 reqwest 响应会挂起?

是否有适当的方法在参考 1D 中转换 2D 数组

传递 Option<&mut T> 时何时需要 mut

为什么我返回的 impl Trait 的生命周期限制在其输入的生命周期内?

您不能borrow 对只读值的可变引用