Как мне сделать формат! вернуть & str из условного выражения? - PullRequest
0 голосов
/ 16 января 2019

Я столкнулся с этой проблемой, когда 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() в конце каждого литерала в шаблоне, как это не так чисто.

Есть ли способ решить проблему, или мне просто нужно обойти ее?

Ответы [ 2 ]

0 голосов
/ 16 января 2019

format! не может вернуть &str, потому что он всегда будет выделять String. Что можно сделать, это вернуть &str из String, что вы и сделали в своем коде.

Как намекнул компилятор, созданный String сразу же отбрасывается после его создания, потому что он вышел из текущей области видимости, и одним из способов может быть внешняя переменная, которая не привязана к области действия match. E.g.:

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);
}
0 голосов
/ 16 января 2019

Это 90% дубликат Возвращает локальную строку в виде среза (& str) , см. Это для нескольких других решений.

Есть еще одна возможность, поскольку все это в одной функции: вы можете объявить переменную для 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, которое обрабатывается только компилятором, а не конкретным типом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...