I want to build a string s
by iterating over a vector of simple structs, appending different strings to acc
depending on the struct.
#[derive(Clone, Debug)]
struct Point(Option<i32>, Option<i32>);
impl Point {
fn get_first(&self) -> Option<i32> {
self.0
}
}
fn main() {
let mut vec = vec![Point(None, None); 10];
vec[5] = Point(Some(1), Some(1));
let s: String = vec.iter().fold(
String::new(),
|acc, &ref e| acc + match e.get_first() {
None => "",
Some(ref content) => &content.to_string()
}
);
println!("{}", s);
}
Running this code results in the following error:
error: borrowed value does not live long enough
Some(ref content) => &content.to_string()
^~~~~~~~~~~~~~~~~~~
note: reference must be valid for the expression at 21:22...
|acc, &ref e| acc + match e.get_first() {
^
note: ...but borrowed value is only valid for the expression at 23:33
Some(ref content) => &content.to_string()
^~~~~~~~~~~~~~~~~~~~
The problem is that the lifetime of the &str
I create seems to end immediately. However, if to_string()
would have returned a &str
in the first place, the compiler would not have complained. Then, what is the difference?
How can I make the compiler understand that I want the string references to live as long as I am constructing s
?
There is a difference between the result of your branches:
""
is of type &'static str
content
is of type i32
, so you are converting it to a String
and then from that to a &str
... but this &str
has the same lifetime as the String
returned by to_string
, which dies too earlyA quick work-around, as mentioned by @Dogbert, is to move acc +
inside the branches:
let s: String = vec.iter().fold(
String::new(),
|acc, &ref e| match e.get_first() {
None => acc,
Some(ref content) => acc + &content.to_string(),
}
);
However, it's a bit wasteful, because every time we have an integer, we are allocating a String
(via to_string
) just to immediately discard it.
A better solution is to use the write!
macro instead, which just appends to the original string buffer. This means there are no wasted allocations.
use std::fmt::Write;
let s = vec.iter().fold(
String::new(),
|mut acc, &ref e| {
if let Some(ref content) = e.get_first() {
write!(&mut acc, "{}", content).expect("Should have been able to format!");
}
acc
}
);
It's maybe a bit more complicated, notably because formatting adds in error handling, but is more efficient as it only uses a single buffer.