Comment convertir une chaîne en une chaîne statique & '

89

Comment convertir un Stringen un &str? Plus précisément, je voudrais le convertir en un stravec la staticdurée de vie ( &'static str).

Christoph
la source
Cela ne semble ni possible ni souhaitable. 'staticdurée de vie impliquerait que la chaîne ne soit jamais désallouée, c'est-à-dire une fuite de mémoire. Pourquoi avez-vous besoin &'static strau lieu de &'a strcertains appropriés 'a?
3
À quoi cela ressemblerait-il pour le convertir en &'a str alors?
Christoph
Via as_slice. Il serait plus facile d'aider si vous décriviez le problème concret que vous essayez de résoudre et les problèmes que vous rencontrez en le faisant.
Notez également SendStr, un type qui est soit une chaîne possédée, soit une chaîne statique.
Chris Morgan

Réponses:

132

Mis à jour pour Rust 1.0

Vous ne pouvez pas obtenir &'static strd'un Stringcar Strings peut ne pas vivre toute la durée de vie de votre programme, et c'est ce que &'staticsignifie la vie. Vous ne pouvez paramétrer une tranche que parString propre durée de vie.

Pour passer d'une Stringtranche à une autre, &'a strvous pouvez utiliser la syntaxe de découpage:

let s: String = "abcdefg".to_owned();
let s_slice: &str = &s[..];  // take a full slice of the string

Alternativement, vous pouvez utiliser le fait qui Stringimplémente Deref<Target=str>et effectue un réemprunt explicite:

let s_slice: &str = &*s;  // s  : String 
                          // *s : str (via Deref<Target=str>)
                          // &*s: &str

Il existe même une autre méthode qui permet une syntaxe encore plus concise, mais elle ne peut être utilisée que si le compilateur est capable de déterminer le type de cible souhaité (par exemple dans des arguments de fonction ou des liaisons de variables typées explicitement). Elle s'appelle deref coercion et permet d'utiliser uniquement l' &opérateur, et le compilateur insérera automatiquement une quantité appropriée de *s en fonction du contexte:

let s_slice: &str = &s;  // okay

fn take_name(name: &str) { ... }
take_name(&s);           // okay as well

let not_correct = &s;    // this will give &String, not &str,
                         // because the compiler does not know
                         // that you want a &str

Notez que ce modèle n'est pas unique pour String/ &str- vous pouvez l'utiliser avec chaque paire de types qui sont connectés Deref, par exemple, avec CString/ CStret OsString/ OsStrdepuis le std::ffimodule ou PathBuf/ Pathdepuis le std::pathmodule.

Vladimir Matveev
la source
26
Dans Rust 1.10 au lieu de let s_slice: &str = &s[..];vous pouvez simplement faire ceci:let s_slice: &str = s.as_str();
Shnatsel
3
Parfois, la chaîne d'origine ne vit pas assez, comme dans un bloc match {...}. Cela conduira à un 's' does not live long enough error.
Dereckson
38

Vous pouvez le faire, mais cela implique une fuite de la mémoire du fichierString . Ce n'est pas quelque chose que vous devez faire à la légère. En fuyant la mémoire du String, nous garantissons que la mémoire ne sera jamais libérée (donc la fuite). Par conséquent, toute référence à l'objet interne peut être interprétée comme ayant la 'staticdurée de vie.

fn string_to_static_str(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

fn main() {
    let mut s = String::new();
    std::io::stdin().read_line(&mut s).unwrap();
    let s: &'static str = string_to_static_str(s);
}
oli_obk
la source
7
Stringgarantit que tant que l'objet n'a pas été abandonné, la mémoire reste vivante. Puisque les mem::forgetgaranties que l'objet ne sera jamais abandonné, nous avons la garantie que la référence au contenu strne sera jamais invalide. Ainsi, nous pouvons affirmer qu'il s'agit d'une 'staticréférence
oli_obk
1
Cela a été extrêmement utile pour mon application Rust, qui devait forcer un Stringdans un &'static strafin que les jetons créés à partir de l'original Stringsoient disponibles sur tous les threads. Sans cela, le compilateur Rust se plaindrait que ma Stringvie se terminait à la fin de la fonction principale, ce qui n'était pas suffisant car il n'avait pas la 'staticgarantie.
mmstick
1
@mmstick: la meilleure solution dans ce cas serait d'utiliser crossbeamet de cibler les threads
oli_obk
3
@mmstick: si vous placez toute votre application dans une portée crossbeam et créez la chaîne en dehors de la portée, vous obtenez exactement cela.
oli_obk
1
Cette réponse est géniale! Cela m'a dit à la fois comment créer sournoisement une tranche de corde statique et m'a convaincu de ne pas le faire! J'ai choisi de refactoriser mon application pour ne pas utiliser de tranches de chaîne statiques dans autant d'endroits.
Paul Chernoch
21

À partir de la version 1.26 de Rust, il est possible de convertir un Stringen &'static strsans utiliser de unsafecode:

fn string_to_static_str(s: String) -> &'static str {
    Box::leak(s.into_boxed_str())
}

Cela convertit l' Stringinstance en une boîte stret la fuit immédiatement. Cela libère toute capacité excédentaire que la chaîne peut actuellement occuper.

Notez qu'il y a presque toujours des solutions qui sont préférables aux objets qui fuient, par exemple en utilisant la crossbeamcrate si vous voulez partager l'état entre les threads.

Sven Marnach
la source
2

TL; DR: vous pouvez obtenir un &'static strde a Stringqui lui-même a une 'staticdurée de vie.

Bien que les autres réponses soient correctes et très utiles, il existe un cas de bord (pas si utile), où vous pouvez en effet convertir un Stringen un &'static str:

La durée de vie d'une référence doit toujours être plus courte ou égale à la durée de vie de l'objet référencé. C'est-à-dire que l'objet référencé doit vivre plus longtemps (ou autant de temps) que la référence. Puisque 'staticsignifie toute la durée de vie d'un programme, une durée de vie plus longue n'existe pas. Mais une durée de vie égale sera suffisante. Donc, si un Stringa une durée de vie de 'static, vous pouvez en obtenir une &'static strréférence.

La création d'un statictype Stringest théoriquement devenue possible avec Rust 1.31 lorsque la const fnfonctionnalité a été publiée. Malheureusement, la seule fonction const retournant a Stringest String::new()actuellement, et elle est toujours derrière une porte de fonctionnalité (donc Rust nightly est requis pour le moment).

Ainsi, le code suivant effectue la conversion souhaitée (en utilisant la nuit) ... et n'a en fait aucune utilité pratique, sauf pour montrer que c'est possible dans ce cas de bord.

#![feature(const_string_new)]

static MY_STRING: String = String::new();

fn do_something(_: &'static str) {
    // ...
}

fn main() {
    do_something(&MY_STRING);
}
Zargony
la source