【问题标题】:Iterator Find, Temporary Value Dropped While Still Borrowed迭代器查找,在仍然借用时丢弃的临时值
【发布时间】:2017-11-20 16:53:12
【问题描述】:

我有一个以常量形式定义的数组,在任何函数之外:

const VALUES: [(char, &str); 2] = [('A', "abc"), ('B', "acb")];

我正在尝试使用位于Iterator 上的find() 方法,以便根据谓词从数组中提取单个元素:

VALUES.iter().find(|&&(name, _)| name == 'A');

在这种形式下,它可以正常工作。但是,一旦我尝试创建let 绑定并尝试绑定结果,我就无法将找到的元素评估为任何东西,根据文档,结果应该以Option<T> 的形式返回。

让我们把第二行改成行不通的:

const VALUES: [(char, &str); 2] = [('A', "abc"), ('B', "acb")];

fn main() {
    let result = VALUES.iter().find(|&&(name, _)| name == 'A');
}

(Playground)

人们希望这会根据文档返回 Option<T>,但我却得到了一个编译错误:

error: borrowed value does not live long enough
 --> src/main.rs:4:63
  |
4 |     let result = VALUES.iter().find(|&&(name, _)| name == 'A');
  |                  ------ temporary value created here          ^ temporary value dropped here while still borrowed
5 | }
  | - temporary value needs to live until here
  |
  = note: consider using a `let` binding to increase its lifetime

我完全糊涂了;我确定我刚刚搞砸了“借用检查器”。也许有人可以指出我正确的方向?

【问题讨论】:

    标签: arrays iterator find rust


    【解决方案1】:

    问题在于您将该数组转换为迭代器的特定方式。

    首先,Rust 中的consts 实际上并不存在于任何地方。相反,它们在使用它们的任何地方都被值替换。因此,每次使用常量时,都会得到它的新副本。

    其次,您使用的是IntoIterator::into_iter。这会按值获取主题并将其转换为迭代器。

    这些与第三部分相结合:IntoIterator 为固定大小的数组实现。它仅针对固定大小数组的 指针 实现。因此,为了调用into_iter,编译器必须插入调用者的自动借用。

    那么,实际发生的事情是这样的:

    let t = {
        // Make a new copy of `VALUES`.
        let values: [(char, &str); 5] = VALUES;
        // Borrow it.
        let values: &[_] = &values;
        // Call `into_iter` on the borrow.
        IntoIterator::into_iter(values).find(|&&(name, _)| name == 'A')
    };
    

    这会导致问题,因为编译器必须同时复制借用VALUES 以获得迭代器。像所有临时对象一样,所说的副本只与语句一样长,但借用(通过绑定到变量)必须比这更长。

    最好的解决方案是使VALUES 成为数组的指针。这可以防止复制整个数组;相反,您只需在每次使用时复制指针。

    const VALUES: &[(char, &str)] = &[...];
    

    或者,您可以显式复制VALUES 并将其存储在一个变量中,然后在那个上使用into_iter。但是,和以前一样,这会引入不必要的复制。

    【讨论】:

    • 常量数组实际上存在于内存中。 This code 有效。被丢弃的临时值不是数组,而是数组切片。
    • @red75prime:我的理解是,那个行为是一种特殊情况,即立即借用的临时对象被提升,因为这种模式相对常见,否则会更痛苦。
    • 另一种选择是使用static,它实际上确实存在于内存中,因此可以正常借用。
    【解决方案2】:

    DK.'s answer 是正确的,但我建议进行更简单的更改以使您的代码正常工作 - 使用 static 而不是 const

    来自documentation about static and const

    更具体地说,Rust 中的常量在内存中没有固定地址。

    静态项目在使用时不会内联。这意味着每个值只有一个实例,并且位于内存中的固定位置。

    切换到static 可以让你获得你正在迭代的东西的内存地址。

    static VALUES: [(char, &str); 2] = [('A', "abc"), ('B', "acb")];
    
    fn main() {
        let result = VALUES.iter().find(|&&(name, _)| name == 'A');
    }
    

    这里,resultOption<&(char, &str)>

    【讨论】:

      猜你喜欢
      • 2017-08-22
      • 1970-01-01
      • 1970-01-01
      • 2021-03-05
      • 2015-12-23
      • 1970-01-01
      • 2023-03-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多