匹配表达式格式!()的生命周期太短

时间:2016-12-20 23:44:55

标签: rust

我正在为我自己的结构实现一个自定义Display::fmt,它代表一个区间。

struct Range<T> {
    lower: Option<T>,
    upper: Option<T>,
}

范围可以是Range { lower: Some(1), upper: None },这意味着它包含从1到无穷大的所有整数(或者我认为的i32的限制)。

如果绑定不是Display::fmt,我想实现T使用Display::fmt&#39; s None,否则显示空字符串:

let range = Range { lower: Some(1), upper: None }
println!("{}", range); // Prints <1,>

let range = Range { lower: Some(1), upper: Some(10) }
println!("{}", range); // Prints <1,10>

let range = Range { lower: None, upper: Some(10) }
println!("{}", range); // Prints <,10>

我已经开始实施,但是match表达式和format!()生成的字符串的生命周期有问题。我的实现问题是格式返回的字符串不能长时间使用,以便进一步使用。

fn main() {
    let opt = Some(1);

    let opt_display = match opt {
        Some(x) => &format!("{}", x), // error: borrowed value does not live long enough
        None => "",
    };

    println!("opt: {}", opt_display);
}

为什么我的方法不起作用,什么是解决我问题的好方法?

3 个答案:

答案 0 :(得分:5)

我不是终身专家,但我相信这里的问题是你试图从匹配中&String创建的String返回format!。由于格式的范围仅在范围内,借用检查员会抱怨。

要解决此问题,您可以使用拥有的字符串。

fn main() {
    let opt = Some(1);

    let opt_display = match opt {
        Some(ref x) => format!("{}", x), // Allowed since opt_display now owns the string
        None => "".into(),
    };
    // Another way to achieve the same thing.
    //let opt_display = opt.map(|s| format!("{}", s)).unwrap_or("".into());

    println!("opt: {}", opt_display);
}

答案 1 :(得分:3)

实现Display时,不需要返回字符串;您只需write!()进入提供的格式化程序。

看起来像是:

impl<T: Display> Display for Range<T> {
    fn fmt(&self, fmt: &mut Formatter) -> Result<(), std::fmt::Error> {
        write!(fmt, "<")?;
        if let Some(v) = self.lower {
            write!(fmt, "{}", v)?;
        }
        write!(fmt, ",")?;
        if let Some(v) = self.upper {
            write!(fmt, "{}", v)?;
        }
        write!(fmt, ">")
    }
}

Playground

答案 2 :(得分:2)

作为Emilgardis has already explained,您尝试返回对引用仍然存在时将丢弃的值的引用。恭喜你,你只是试图创造内存不安全,这会导致C或C ++崩溃(或更糟),但Rust阻止了它!

您可以提高效率只是在一种情况下进行分配:

fn main() {
    let opt = Some(1);
    let opt_display = opt.map(|s| format!("{}", s));

    // Type not needed, only used to assert the type is what we expect
    let opt_display_str: &str = opt_display.as_ref().map(String::as_str).unwrap_or("");
    println!("opt: {}", opt_display_str);
}

您还可以使用Cow,它允许拥有或借用字符串。请注意它与其他答案的相似程度,但在None的情况下,这不会分配:

use std::borrow::Cow;

fn main() {
    let opt = Some(1);

    let opt_display: Cow<str> = match opt {
        Some(ref x) => format!("{}", x).into(),
        None => "".into(),
    };

    println!("opt: {}", opt_display);
}
  

我想实施Display::fmt

最佳要做的事情可能是避免任何分配。您将获得write!的格式化程序,只需在每个匹配组中调用write!即可。这可能会引入一些重复,但可能更有效。没有格式化程序,它看起来像:

fn main() {
    let opt = Some(1);

    print!("opt: ");

    if let Some(ref x) = opt  {
       print!("{}", x);
    }

    println!("");    
}

在格式化程序中替换write!(f, print!(并返回错误。