我希望通过遍历简单结构的向量来构建字符串s
,根据结构将不同的字符串附加到acc
。
#[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);
}
运行此代码会导致以下错误:
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()
^~~~~~~~~~~~~~~~~~~~
问题是我创建的&str
的生命周期似乎立即结束。但是,如果to_string()
首先返回&str
,则编译器不会抱怨。那么,有什么区别?
如果我正在构建s
,我怎样才能使编译器理解我希望字符串引用生效?
答案 0 :(得分:6)
您的分支结果之间存在差异:
""
的类型为&'static str
content
属于i32
类型,因此您要将其转换为String
,然后再将其转换为&str
...但此&str
与String
返回的to_string
具有相同的生命周期,而acc +
过早死亡正如@Dogbert所提到的,快速解决方法是将let s: String = vec.iter().fold(
String::new(),
|acc, &ref e| match e.get_first() {
None => acc,
Some(ref content) => acc + &content.to_string(),
}
);
移到分支机构内:
String
然而,这有点浪费,因为每次我们有一个整数时,我们都会分配to_string
(通过write!
)只是为了立即丢弃它。
更好的解决方案是使用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
}
);
宏,它只是附加到原始字符串缓冲区。这意味着没有浪费的分配。
var user = require('./users');
router.post('/login', user.post('/login'));
它可能有点复杂,特别是因为格式化会增加错误处理,但是因为它只使用单个缓冲区而更有效。
答案 1 :(得分:3)
您的问题有多种解决方案。但首先是一些解释:
如果
to_string()
首先返回&str
,则编译器不会抱怨。那么,有什么区别?
假设有一个方法to_str()
返回&str
。签名会是什么样的?
fn to_str(&self) -> &str {}
为了更好地理解这个问题,我们可以添加明确的生命周期(由于终身省略而不是必需的):
fn to_str<'a>(&'a self) -> &'a str {}
很明显,只要方法的接收者(&str
),返回的self
就会存在。这可以,因为接收器的寿命足以进行acc + ...
操作。但是,在你的情况下,.to_string()
调用会创建一个新对象,它只存在于第二个匹配臂中。手臂的身体离开后,它将被摧毁。因此,您无法将对它的引用传递给外部作用域(acc + ...
发生)。
所以一个可能的解决方案如下所示:
let s = vec.iter().fold(
String::new(),
|acc, e| {
acc + &e.get_first()
.map(|f| f.to_string())
.unwrap_or(String::new())
}
);
这不是最佳的,但幸运的是,您的默认值是一个空字符串,并且拥有的空字符串版本(String::new()
)不需要任何堆分配,因此不会有性能损失。
但是,我们仍然每个整数分配一次。要获得更有效的解决方案,请参阅Matthieu M.'s answer。