Je suis tombé sur ce problème où format!
crée une valeur temporaire dans un modèle qui n'est ancré à rien, pour autant que je sache.
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)
Dans ce code, le type de catégorie
est un & str
, qui se satisfait en renvoyant un littéral comme "Entre 0 et 9"
. Si je veux formater la valeur correspondante en une tranche en utilisant as_str ()
, alors j'obtiens une erreur:
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);
J'ai fait quelques lectures, et j'ai trouvé des personnes avec des problèmes similaires, mais je ne semble pas trouver de solution.
Une solution de contournement simple serait que category
soit une String
au lieu d'un & str
, mais je n'aime pas l'idée de devoir mettre .to_string ()
à la fin de chaque littéral du motif, car ce n'est pas aussi propre .
Existe-t-il un moyen de résoudre le problème ou dois-je simplement le contourner?
3 Réponses :
Il s'agit à 90% d'un doublon de Renvoyer la chaîne locale sous forme de tranche (& str) , voir cela pour plusieurs autres solutions .
Il y a une possibilité supplémentaire car tout cela est dans une seule fonction: vous pouvez déclarer une variable pour la String
et ne la définir que lorsque vous avez besoin d'allouer. Le compilateur (obliquement) suggère ceci:
Pensez à utiliser une liaison
let
pour créer une valeur plus durable
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); }
C'est la même chose que d'utiliser un Cow
, juste géré par le compilateur au lieu d'un type spécifique.
p >
format!
ne peut pas renvoyer & str
car il allouera toujours String
. Ce qu'il est possible de faire est de renvoyer un & str
à partir d'une String
, ce que vous avez fait dans votre code.
Comme le compilateur l'a indiqué, le créé La chaîne
est immédiatement supprimée après sa création car elle est sortie de la portée actuelle et un moyen de contourner pourrait être une variable externe qui n'est pas liée à la portée match
. Par exemple:
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); }
Si vous voulez un environnement [no_std]
ou ne voulez pas faire d'allocation dynamique, vous pouvez jeter un œil à ceci extrait de code limité:
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); }
Vous pouvez utiliser write! (& Mut buffer, "{}", number)
pour enregistrer une allocation + copie dans le premier extrait, et je pense que cela fonctionnera également sous no_std
avec un tampon fixe (non testé).
Comment écrire un entier sous forme de chaîne dans un tableau d'octets avec no_std?
Dans mon cas, Comment faire surmonter la "perte de valeur temporaire lors de l'emprunt" lors de la conversion d'un i32 en & str
Je pourrais le résoudre en déplaçant l'appel dans les branches
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), } }
Un peu ce qui est suggéré dans le message d'erreur https://doc.rust-lang.org/error-index.html#E0597