Option::map的结果存活时间不够长

3
我原本以为下面这两个函数是等价的,但第一个函数无法编译。
pub fn does_not_work<I: IntoIterator>(values: I)
where
    I::Item: AsRef<str>,
{
    if let Some(value) = values.into_iter().nth(0).map(|item| item.as_ref()) {
        if value == "first" {
            println!("This should print");
        }
    }
}

pub fn does_work<I: IntoIterator>(values: I)
where
    I::Item: AsRef<str>,
{
    if let Some(value) = values.into_iter().nth(0) {
        if value.as_ref() == "first" {
            println!("This should print");
        }
    }
}

fn main() {
    does_work(&["first"]);
}

编译错误是:
error[E0597]: `item` does not live long enough
 --> src/main.rs:5:63
  |
5 |     if let Some(value) = values.into_iter().nth(0).map(|item| item.as_ref()) {
  |                                                               ^^^^        - `item` dropped here while still borrowed
  |                                                               |
  |                                                               borrowed value does not live long enough
...
9 |     }
  |     - borrowed value needs to live until here

代码已经修改,比实际情况更加简洁明了,以更清楚地说明重点。为了澄清为什么我想使用第一种方法,我的实际代码中使用了很多次value,我不想让每一个后面都跟着一个.as_ref()
有没有办法使它能够运作,或者Option::map不是这种情况的好选择?是否有其他简洁的方法来解决这个问题?
2个回答

4
当你创建一个新的迭代器时,旧的值就不再可用了。但是你需要旧的值存在才能返回Some(value)。在你的情况下,你把一个&[&'static str]传递给函数,所以它保证会存在足够长的时间,但根据类型你也可以传递&[String]
在这种情况下,原始的String可能会被释放,然后你会得到一个悬空指针。如果不调用.as_ref(),你就保证了原始值会在Some(value)中可用。
如果你只是想跳过多个.as_ref()调用,你可以这样做:
pub fn does_work<I: IntoIterator>(values: I)
where
    I::Item: AsRef<str>,
{
    if let Some(value) = values.into_iter().next() {
        let s = value.as_ref();
        if s == "first" {
            println!("This should print");
        }
    }
}

我不明白为什么在一行后调用.as_ref()是安全的,而在if let中调用则不安全。 - skyler
1
这与所在的代码块有关,而不是行号。 在您的 does_not_work 示例中,map 之后 item 不再存在,因此无法返回其先前值的任何指针。 在您的第二个示例和我的答案中,valueitem 是相同的。 s 只是转换后的 value - 它们都存在于同一个块中(直到 if 结束)。 - viraptor

2
那是因为map会获取参数item的所有权,所以在返回后它将被销毁。这使得结果引用无效。
在使用map之前,您应该应用Option::as_refOption<T>转换为Option<&T>,例如:
pub fn does_not_work<I: IntoIterator>(values: I)
where
    I::Item: AsRef<str>,
{
    if let Some(value) = values.into_iter().next().as_ref().map(|item| item.as_ref()) {
        if value == "first" {
            println!("This should print");
        }
    }
}

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