Result of Option::map does not live long enough

我只是一个虾纸丫 提交于 2019-12-12 12:45:02

问题


I expected the two functions below to be equivalent. However the first one does not compile.

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"]);
}

The compile error is:

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

The code is altered so as to be less verbose than the actual context it comes from and to more clearly illustrate the point. To clarify why I want to use the first approach, I use value many more times in my actual code and I don't want to have every single one of them followed by a .as_ref().

Is there a way to make this work or is Option::map not a good choice for this situation? Is there another concise way to solve this problem?


回答1:


When you create a new iterator, old values are not available anymore. But you need the old value to exist in order to return Some(value). In your case, you're passing a &[&'static str] to the function, so it's guaranteed to stay around long enough, but according to the types you could just as well pass &[String].

In that case, the original String could be freed and you'd be left with a dangling pointer. By not calling .as_ref(), you guarantee that the original value will be available in the Some(value).

If you just want to skip multiple .as_ref() calls you can instead do:

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");
        }
    }
}



回答2:


That's because map takes ownership of the parameter item, so it will be destroyed after it returns. This makes the result reference invalid.

You should apply Option::as_ref to transform Option<T> into Option<&T> before using map, like this:

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");
        }
    }
}


来源:https://stackoverflow.com/questions/35249309/result-of-optionmap-does-not-live-long-enough

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!