折叠匹配内的引用会导致生命周期错误

lsu*_*und 1 functional-programming lifetime rust borrowing

我想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);

}
Run Code Online (Sandbox Code Playgroud)

运行此代码会导致以下错误:

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()
                                 ^~~~~~~~~~~~~~~~~~~~
Run Code Online (Sandbox Code Playgroud)

问题是&str我创造的生命周期似乎立即结束.但是,如果to_string()首先返回a &str,则编译器不会抱怨.那么,有什么区别?

我怎样才能使编译器理解我希望字符串引用只要我构建就可以存活s

Mat*_* M. 6

您的分支结果之间存在差异:

  • "" 是类型的 &'static str
  • content是类型的i32,所以你将它转换为a String然后从那个转换为&str...但是它&strString返回的生命周期相同to_string,过早死亡

正如@Dogbert所提到的,快速解决方法是acc +进入分支机构:

let s: String = vec.iter().fold(
    String::new(),
    |acc, &ref e| match e.get_first() {
        None => acc,
        Some(ref content) => acc + &content.to_string(),
    }
);
Run Code Online (Sandbox Code Playgroud)

但是,它有点浪费,因为每次我们有一个整数,我们分配一个String(via 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
    }
);
Run Code Online (Sandbox Code Playgroud)

它可能有点复杂,特别是因为格式化会增加错误处理,但由于它只使用单个缓冲区,因此效率更高.

  • 您可以使用`std :: fmt :: Write`将`String`与`write!()`宏一起使用.这使代码更短.我也更喜欢`if let Some(ref content)= e.get_first(){write!(...); } acc`避免"返回"`acc`两次.. (2认同)