问题
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