我如何制作格式!从条件表达式返回&str?

时间:2019-01-16 18:07:45

标签: string scope rust lifetime temporary

我碰到了这个问题,据我所知,format!在一个不固定于任何内容的模式中创建了一个临时值。

let x = 42;
let category = match x {
    0...9 => "Between 0 and 9",
    number @ 10 => format!("It's a {}!", number).as_str(),
    _ if x < 0 => "Negative",
    _ => "Something else",
};

println!("{}", category);

在此代码中,category的类型为&str,这可以通过返回类似"Between 0 and 9"的文字来满足。如果我想使用as_str()将匹配的值格式化为切片,则会出现错误:

error[E0716]: temporary value dropped while borrowed
 --> src/main.rs:5:24
  |
3 |     let category = match x {
  |         -------- borrow later stored here
4 |         0...9 => "Between 0 and 9",
5 |         number @ 10 => format!("It's a {}!", number).as_str(),
  |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^        - temporary value is freed at the end of this statement
  |                        |
  |                        creates a temporary which is freed while still in use
  |
  = note: consider using a `let` binding to create a longer lived value
  = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

我已经读了一些书,发现有类似问题的人,但是我似乎找不到任何解决方法。

一个简单的解决方法是让category成为String而不是&str,但是我不喜欢必须将.to_string()放在模式中每个文字的结尾,因为它不是那么干净。

是否有解决问题的方法,还是我只需要解决它?

3 个答案:

答案 0 :(得分:3)

这是Return local String as a slice (&str)的90%重复,请参见其他解决方案的说明。

还有另一种可能,因为这是一个功能:您可以为String声明一个变量,并且仅在需要分配时设置它。编译器(倾斜地)建议:

  

考虑使用let绑定来创建寿命更长的值

fn main() {
    let x = 42;
    let tmp;

    let category = match x {
        0...9 => "Between 0 and 9",
        number @ 10 => {
            tmp = format!("It's a {}!", number);
            &tmp
        }
        _ if x < 0 => "Negative",
        _ => "Something else",
    };

    println!("{}", category);
}

这与使用Cow几乎相同,只是由编译器而不是特定类型来处理。

答案 1 :(得分:1)

format!无法返回&str,因为它将始终分配String。可以做的是从&str返回一个String,这就是您在代码中所做的。

正如编译器所提示的那样,创建的String在创建之后会立即被删除,因为它超出了当前范围,并且一种解决方法可能是不受限于match范围的外部变量。例如:

use std::fmt::Write;

fn main() {
    let mut buffer = String::with_capacity(20);
    buffer.push_str("It's a ");

    let x = 10;
    let category = match x {
        0...9 => "Between 0 and 9",
        number @ 10 => {
            write!(&mut buffer, "{}", number).unwrap();
            buffer.as_str()
        }
        _ if x < 0 => "Negative",
        _ => "Something else",
    };

    println!("{}", category);
}

如果您想要一个[no_std]环境或不想进行任何动态分配,则可以查看以下受限代码段:

use core::str;

fn each_digit<F>(mut number: u32, mut f: F)
where
    F: FnMut(u8),
{
    while number > 0 {
        f((number % 10) as u8);
        number /= 10;
    }
}

fn main() {
    const BUFFER_LEN: usize = 20;
    let mut buffer = [0u8; BUFFER_LEN];

    let x = 12344329;
    let category = match x {
        0...9 => "Between 0 and 9",
        number @ 123443219 => {
            let mut idx = BUFFER_LEN;
            each_digit(number, |digit| {
                let ascii = digit + 48;
                idx -= 1;
                buffer[idx] = ascii;
            });
            str::from_utf8(&buffer[idx..BUFFER_LEN]).unwrap()
        },
        _ => "Something else",
    };

    assert_eq!("123443219", category);
}

答案 2 :(得分:0)

就我而言,How to overcome "temporary value dropped while borrowed" when converting an i32 to &str
我可以通过在分支内移动呼叫来解决它

pub fn uidl(&mut self, message_number: Option<i32>) -> POP3Result {
    let command = match message_number {
        Some(_) => POP3Command::UidlOne,
        None => POP3Command::UidlAll,
    };

    match message_number {
        Some(i) => {
            // Here the value is not dropped because it is not leaving the scope
            self.execute_command(command, Some(arg.to_string().as_str()))
        }
        // Here I had to duplicate the call
        None => self.execute_command(command, None),
    }
}

关于错误消息https://doc.rust-lang.org/error-index.html#E0597的建议