我如何制作格式! return 来自条件表达式的 &str?

How do I make format! return a &str from a conditional expression?

我遇到了这个问题,据我所知,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()在模式中每个文字的末尾,因为它不那么干净。

有没有办法解决这个问题,还是我只需要解决这个问题?

这是 的 90% 重复,请参阅其他多个解决方案。

还有一种额外的可能性,因为这一切都在一个函数中:您可以为 String 声明一个变量,并且只在需要分配时设置它。编译器(倾斜地)建议:

consider using a let binding to create a longer lived value

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 基本相同,只是由编译器处理,而不是特定类型。

另请参阅:

format! 不能 return &str 因为它总是会分配 String。可以做的是 return 从 String&str,这就是您在代码中所做的。

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

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);
}

以我为例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