尝试改变返回 Iterator 的闭包内的状态时,出现 Rust 错误“无法推断借用表达式的适当生命周期”

2023-12-02

我正在尝试学习 Rust,并在尝试模拟嵌套 Python 生成器时遇到了与生命周期相关的问题。正如编译器所报告的,问题在于由闭包改变的值的生命周期。代码的关键是 flat_mapping 一个闭包,它调用一个函数,该函数在其返回的迭代器中改变从外部范围提供的值。参见第 39 行Rust 游乐场示例.

这里的代码是原始程序的简化版本。由于我的最终目标是更多地了解 Rust,因此我更希望获得一些见解,而不是修复我的代码!

例如,一个“解决方案”是第 44 行注释掉的代码。它“有效”,但由于总是分配一个Vec包含轨迹上的所有点,即使用户只想检查轨迹上的第一个点。

我认为这个问题与可变借用方式有关point继续存在于迭代器中trace_steps返回。我尝试了太多的变体,无法在这里列出,从传递point变异自main(更类似于如何trace_step作品)尝试盲目使用Rc<RefCell<Point>>当我开始绝望的时候。

下面是从下面复制的代码铁锈游乐场 is:

#[derive(Debug, Eq, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn new(x: i32, y: i32) -> Point {
        Point { x, y }
    }
}

// Intention is that this is like a Python generator.  Normally the "step" would
// be a struct with a direction and a length but this is a simplified version.
fn trace_step<'a>(point: &'a mut Point, step: u8) -> impl Iterator<Item = Point> + 'a {
    let mut len = step;
    std::iter::from_fn(move || {
        if len == 0 {
            None
        } else {
            len -= 1;
            point.x += 1;
            Some(Point { ..*point })
        }
    })
}

// FIXME: See compiler error!!!
// Compiler cannot infer an appropriate lifetime for the borrow &mut point.
// Can't the borrow just live as long as the closure?
//
// Intention is that this produces points along a path defined by multiple
// steps.  Simplified.
fn trace_steps(steps: Vec<u8>) -> impl Iterator<Item = Point> {
    let mut point: Point = Point::new(0, 0);

    // FIXME: This doesn't work.
    let f = |x: &u8| trace_step(&mut point, *x);
    steps.iter().flat_map(f)

    // This works, but we don't want to commit to allocating the space for all
    // points if the user only needs to, for example, count the number of points.
    /*
    let mut ret: Vec<Point> = Vec::new();
    for step in steps {
        ret.extend(trace_step(&mut point, step));
    }
    ret.into_iter()
    */
}

fn main() {
    let mut point: Point = Point::new(0, 0);
    let points: Vec<Point> = trace_step(&mut point, 3).collect();

    // Outputs: [Point { x: 1, y: 0 }, Point { x: 2, y: 0 }, Point { x: 3, y: 0 }]
    println!("{:?}", points);

    // Should trace the first from (0, 0) to (1, 0) and then trace the second step
    // from (1, 0) to (2, 0) to (3, 0).
    let points: Vec<Point> = trace_steps(vec![1, 2]).collect();
    println!("{:?}", points);
}

以及在以下位置运行时出现的错误铁锈游乐场 is:

   Compiling playground v0.0.1 (/playground)
error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
  --> src/main.rs:38:33
   |
38 |     let f = |x: &u8| trace_step(&mut point, *x);
   |                                 ^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime '_ as defined on the body at 38:13...
  --> src/main.rs:38:13
   |
38 |     let f = |x: &u8| trace_step(&mut point, *x);
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that closure can access `point`
  --> src/main.rs:38:33
   |
38 |     let f = |x: &u8| trace_step(&mut point, *x);
   |                                 ^^^^^^^^^^
note: but, the lifetime must be valid for the destruction scope surrounding expression at 34:63...
  --> src/main.rs:34:63
   |
34 |   fn trace_steps(steps: Vec<u8>) -> impl Iterator<Item = Point> {
   |  _______________________________________________________________^
35 | |     let mut point: Point = Point::new(0, 0);
36 | |     
37 | |     // FIXME: This doesn't work.
...  |
49 | |     */
50 | | }
   | |_^
note: ...so that references are valid when the destructor runs
  --> src/main.rs:34:63
   |
34 |   fn trace_steps(steps: Vec<u8>) -> impl Iterator<Item = Point> {
   |  _______________________________________________________________^
35 | |     let mut point: Point = Point::new(0, 0);
36 | |     
37 | |     // FIXME: This doesn't work.
...  |
49 | |     */
50 | | }
   | |_^

error: aborting due to previous error

error: could not compile `playground`.

问题是 Rust 对于复制可变引用非常严格。这是一个问题,因为当你返回里面的迭代器时flat_map,该迭代器必须具有对该点的可变(唯一)引用,但是flat_map不够健壮,无法将迭代器返回给您,因此 Rust 无法证明在再次调用闭包时,最后一个迭代器仍然没有引用该点。一旦发电机稳定下来,正确地做到这一点将是微不足道的。在此期间,这仍然是可能的,但是MUCH比我想象的要难,你需要手动实现Iterator特征。干得好:

游乐场链接

use std::iter::{ExactSizeIterator, FusedIterator};

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn new(x: i32, y: i32) -> Point {
        Self { x, y }
    }
}

#[derive(Debug)]
struct StepTracer<'a> {
    point: &'a mut Point,
    len: u8,
}

impl<'a> StepTracer<'a> {
    fn new(point: &'a mut Point, len: u8) -> Self {
        Self { point, len }
    }

    fn into_inner(self) -> &'a mut Point {
        self.point
    }
}

impl<'a> Iterator for StepTracer<'a> {
    type Item = Point;

    fn next(&mut self) -> Option<Self::Item> {
        if self.len == 0 {
            None
        } else {
            self.len -= 1;
            self.point.x += 1;
            Some(*self.point)
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        (self.len as usize, Some(self.len as usize))
    }
}

impl FusedIterator for StepTracer<'_> {}
impl ExactSizeIterator for StepTracer<'_> {}

// You may also want to consider implementing DoubleEndedIterator
// Additional traits: https://doc.rust-lang.org/std/iter/index.html#traits

enum MultiStepTracerState<'a> {
    First(&'a mut Point),
    Second(&'a mut Point),
    Tracer(StepTracer<'a>),
    Done,
}

/// Intention is that this produces points along a path defined by multiple
/// steps. Simplified.
struct MultiStepTracer<'a, I: Iterator<Item = u8>> {
    steps: I,
    state: MultiStepTracerState<'a>,
}

impl<'a, I: Iterator<Item = u8>> MultiStepTracer<'a, I> {
    fn new(point: &'a mut Point, steps: I) -> Self {
        Self {
            steps,
            state: MultiStepTracerState::First(point),
        }
    }
}

impl<I: Iterator<Item = u8>> Iterator for MultiStepTracer<'_, I> {
    type Item = Point;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            let mut temp_state = MultiStepTracerState::Done;
            std::mem::swap(&mut self.state, &mut temp_state);
            let point_ref = match temp_state {
                MultiStepTracerState::First(point) => {
                    let result = *point;
                    self.state = MultiStepTracerState::Second(point);
                    return Some(result);
                }
                MultiStepTracerState::Second(point) => point,
                MultiStepTracerState::Tracer(mut tracer) => {
                    if let Some(result) = tracer.next() {
                        self.state = MultiStepTracerState::Tracer(tracer);
                        return Some(result);
                    } else {
                        tracer.into_inner()
                    }
                }
                MultiStepTracerState::Done => {
                    return None;
                }
            };

            if let Some(len) = self.steps.next() {
                self.state = MultiStepTracerState::Tracer(StepTracer::new(point_ref, len));
            } else {
                self.state = MultiStepTracerState::Done;
                return None;
            }
        }
    }
}

impl<I: Iterator<Item = u8>> FusedIterator for MultiStepTracer<'_, I> {}

fn main() {
    let mut point: Point = Point::new(0, 0);
    let points: Vec<Point> = StepTracer::new(&mut point, 3).collect();

    // Outputs: [Point { x: 1, y: 0 }, Point { x: 2, y: 0 }, Point { x: 3, y: 0 }]
    println!("{:?}", points);

    // Should trace the first from (0, 0) to (1, 0) and then trace the second step
    // from (1, 0) to (2, 0) to (3, 0).
    let points: Vec<Point> =
        MultiStepTracer::new(&mut Point::new(0, 0), [1, 2].iter().copied()).collect();
    println!("{:?}", points);
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

尝试改变返回 Iterator 的闭包内的状态时,出现 Rust 错误“无法推断借用表达式的适当生命周期” 的相关文章

随机推荐

  • 使用 iconv 进行简单的 UTF8->UTF16 字符串转换

    我想编写一个函数将 UTF8 字符串转换为 UTF16 小端 问题是 iconv函数似乎没有让您提前知道需要多少字节来存储输出字符串 我的解决方案是从分配开始2 strlen utf8 然后运行iconv在循环中 增加该缓冲区的大小real
  • 随机运动pygame

    我正在尝试制作一个简单的生活模拟器 我需要 细胞 在屏幕上几乎随机移动 有一些规则 但问题是 一段时间后 它们往往会聚集在屏幕的左上角 我尝试改变很多事情 比如完全跳过规则并让它们完全随机移动 但它们仍然聚集在一起 我的代码中是否存在一些明
  • 使用jwplayer 6.11视频无法在ipad上播放

    我们使用 JW Player 6 11 来播放 mp4 和 flv 视频 但无法正常工作 ios7 iPhone 我该如何解决 iPad 的这个问题 function playvideo jwplayer mediaplayer setup
  • Android 上的 Snackbar 无需更改主题

    当我在 Android 上创建小吃栏时 出现以下错误 java lang IllegalStateException 您需要在此活动中使用 Theme AppCompat 主题 或后代 我不想将活动的主题更改为 AppCompat 有没有办
  • 从相对路径中的文件加载 C# 中的图片框图像

    我在 Windows 窗体解决方案中有一个图片框图像 用户从数据库中选择一个项目后 我想将图像加载到此图片框中 图像的文件名将来自数据库 并且所有图像必须存储在应用程序文件夹 Images 的子文件夹中 我不想在我的解决方案中包含所有这些
  • Android:如何查找屏幕的宽度和高度?

    我试图找到屏幕的宽度和高度 但我尝试过的方法都不适用于我创建的类 我的代码如下 有谁知道如何找到它 我无法使用下面尝试的方式 因为 getWidth 已被弃用 public class Crate public int acrossCrat
  • 在 LINQ 查询中的模型值中使用列表

    我正处于 ASP NET MVC 开发的非常基础的阶段 因此 有时我很难使用简单的 LINQ 查询来工作 设想 我有一个页面有一些Image并由用户对该图像发表评论 就像 Facebook 上包含用户评论的帖子一样 因此 我从文本区域保存这
  • git merge squash - 当我想要的只是我压扁的分支的更改时冲突解决

    I have github当主分支达到某种可接受的状态时 我将其推送到 github 的分支 已经这样做过一次 为此我做了 MrD MRSD c Dropbox eclipse workspaces android AndroidMonit
  • 为 C++ 指针集定义运算符<

    我正在尝试编写 C STL 集 它保留指向我的自定义类的指针 但我不明白当我使用指针而不是类的对象时如何重载 我读到 如果我们重载 set 用于比较的 当您使用operator 函数创建一个结构并将其传递给集合时 我遇到了这个技巧 例如se
  • Tensorflowpartial_run()“在执行部分运行之前必须运行‘setup’!”尽管已被设立

    我正在使用tensorflow的partial run 方法围绕运行子图构建概念验证 而无需重新计算 目前我有一个简单的小 python 脚本 见下文 它应该将两个占位符值相乘并加 1 作为部分图运行 此操作有效一次 然后失败并出现错误 t
  • 如何分析 goroutine 的数量

    基本上我想知道我的程序是否随着时间的推移而泄漏 goroutine 所以 我想看看随着时间的推移有多少 goroutine 正在运行 有什么办法可以通过pprof 我已经搞定了go tool pprof http localhost 888
  • 如何在 docker 容器上设置 ulimit / 文件描述符 镜像标签为 phusion/baseimage-docker

    我需要在 docker 容器上正确设置文件描述符限制 我使用 ssh 连接到容器 https github com phusion baseimage docker 已经尝试过 编辑limits conf容器忽略这个文件 新贵程序位于htt
  • 如何在空手道框架中编辑配置的标头

    在我的框架中 我有 headers js 文件 我使用命令在每个功能文件的背景上调用该 js 文件 configure headers read headers js 这按预期工作 某些情况下我需要更改 client id 值 例如 hea
  • SSLHandshakeException:收到致命警报:Java 6 -> 8 升级后握手失败

    我们最近将一个项目从 Java 6 更新到了 Java 8 现在我们在 SSL 握手方面遇到了障碍 服务层使用客户端请求和接收来自第三方应用程序的调用 在服务层 密钥库初始化为 System setProperty javax net ss
  • 从数据库中写入和读取最新(按日期)的行

    我对下面的代码有三个问题 基本上我正在尝试以 currentdate count 格式写入行 继续将行日期附加到同一个 csv 文件中 读取最近的 5 个条目并打印它 目前我尝试了以下操作并遇到以下错误 1 如何以当前日期格式写入一行 计数
  • Android 上的 Proguard 与 OrmLite

    我应该如何在 Android 上将 proguard 与 ormlite 库一起使用 尝试这个 keep class com j256 keepclassmembers class com j256 keep enum com j256 k
  • 第 n 个子元素选择错误的元素

    对于这个特定的站点 当我通过 CSS 或 jQuery 使用 nth child 时 nth child 选择器捕获了错误的元素 我在调用的选择器之前有一个孩子 home article nth child 3 captures 2nd c
  • 评估 FeatherJS 身份验证需求

    我和我的同事想构建一个聊天应用程序 ReactJS NodeJS 我们一直在寻找最好的框架来实现这一点 FeathersJS 似乎无疑是最稳定且功能丰富的 socket io 包装器 然而 由于我们希望允许我们的应用程序扩展 因此我们决定将
  • 如何检测字符串列表中的公共子字符串

    给定一组字符串 例如 EFgreen EFgrey EntireS1 EntireS2 J27RedP1 J27GreenP1 J27RedP2 J27GreenP2 JournalP1Black JournalP1Blue Journal
  • 尝试改变返回 Iterator 的闭包内的状态时,出现 Rust 错误“无法推断借用表达式的适当生命周期”

    我正在尝试学习 Rust 并在尝试模拟嵌套 Python 生成器时遇到了与生命周期相关的问题 正如编译器所报告的 问题在于由闭包改变的值的生命周期 代码的关键是 flat mapping 一个闭包 它调用一个函数 该函数在其返回的迭代器中改