Cómo convertir una cadena en una & 'str estática

92

¿Cómo convierto a Stringen a &str? Más específicamente, me gustaría convertirlo en un strcon el statictiempo de vida ( &'static str).

Christoph
fuente
Eso no parece posible ni deseable. 'staticEl tiempo de vida implicaría que la cadena nunca se desasignará, es decir, una pérdida de memoria. ¿Por qué necesitas en &'static strlugar de &'a stralgo apropiado 'a?
3
¿Cómo se vería convertirlo en &'a str entonces?
Christoph
Via as_slice. Sería más fácil ayudar si describiera el problema concreto que está tratando de resolver y los problemas que encuentra al hacerlo.
También tenga en cuenta SendStr, un tipo que es una cadena propia o una cadena estática.
Chris Morgan

Respuestas:

134

Actualizado para Rust 1.0

No puede obtener &'static strde un Stringporque Stringes posible que s no viva durante toda la vida de su programa, y ​​eso es lo que &'staticsignifica vida. Solo puede obtener un segmento parametrizado por su Stringpropia vida a partir de él.

Para pasar de un sector Stringa otro &'a str, puede utilizar la sintaxis de corte:

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

Alternativamente, puede usar el hecho de que Stringimplementa Deref<Target=str>y realiza un représtamo explícito:

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

Existe incluso otra forma que permite una sintaxis aún más concisa, pero solo se puede usar si el compilador puede determinar el tipo de destino deseado (por ejemplo, en argumentos de función o enlaces de variables escritos explícitamente). Se llama coerción deref y permite usar solo el &operador, y el compilador insertará automáticamente una cantidad adecuada de *s según el contexto:

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

Tenga en cuenta que este patrón no es único para String/ &str; puede usarlo con cada par de tipos que estén conectados Deref, por ejemplo, con CString/ CStry OsString/ OsStrdesde std::ffimódulo o PathBuf/ Pathdesde std::pathmódulo.

Vladimir Matveev
fuente
29
En Rust 1.10 en lugar de let s_slice: &str = &s[..];simplemente puedes hacer esto:let s_slice: &str = s.as_str();
Shnatsel
3
En algún momento, la cadena original no vive lo suficiente, como en un bloque de coincidencia {...}. Eso conducirá a un 's' does not live long enough error.
Dereckson
42

Puedes hacerlo, pero implica filtrar la memoria delString . Esto no es algo que deba hacer a la ligera. Al filtrar la memoria del String, garantizamos que la memoria nunca se liberará (por lo tanto, la fuga). Por lo tanto, cualquier referencia al objeto interno puede interpretarse como si tuviera la 'staticvida útil.

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
fuente
8
Stringgarantiza que, mientras el objeto no se haya caído, la memoria permanece viva. Dado mem::forgetque garantiza que el objeto nunca se eliminará, tenemos la garantía de que la referencia al contenido strnunca será inválida. Así podemos afirmar que es una 'staticreferencia
oli_obk
1
Esto fue increíblemente útil para mi aplicación Rust, que necesitaba Stringconvertir a en a &'static strpara que los tokens creados a partir del original Stringestuvieran disponibles en todos los hilos. Sin esto, el compilador de Rust se quejaría de que Stringtenía una vida útil que terminaba al final de la función principal, lo que no era lo suficientemente bueno porque no tenía la 'staticgarantía.
mmstick
1
@mmstick: la mejor solución en ese caso sería usar crossbeamsubprocesos con alcance
oli_obk
3
@mmstick: si pones toda tu aplicación en un alcance transversal y creas la cadena fuera del alcance, obtienes exactamente eso.
oli_obk
1
¡Esta respuesta es genial! ¡Ambos me dijeron cómo crear de manera tortuosa un segmento de cuerda estática y me convencieron de no hacerlo! Elegí refactorizar mi aplicación para no usar cortes de cadenas estáticas en tantos lugares.
Paul Chernoch
24

A partir de la versión 1.26 de Rust, es posible convertir un Stringa &'static strsin usar unsafecódigo:

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

Esto convierte la Stringinstancia en una caja stry la filtra inmediatamente. Esto libera todo el exceso de capacidad que la cadena pueda ocupar actualmente.

Tenga en cuenta que casi siempre hay soluciones que son preferibles a los objetos con fugas, por ejemplo, usar la crossbeamcaja si desea compartir el estado entre subprocesos.

Sven Marnach
fuente
2

TL; DR: puede obtener un &'static strde un Stringque en sí tiene 'statictoda la vida.

Aunque las otras respuestas son correctas y más útiles, hay un caso de borde (no tan útil), donde de hecho puede convertir Stringa en a &'static str:

La vida útil de una referencia siempre debe ser más corta o igual a la vida útil del objeto referenciado. Es decir, el objeto referenciado tiene que vivir más (o el mismo tiempo) que la referencia. Dado que 'staticsignifica toda la vida útil de un programa, no existe una vida útil más larga. Pero una vida igual será suficiente. Entonces, si Stringtiene una vida útil de 'static, puede obtener una &'static strreferencia de él.

Teóricamente, la creación de un statictipo se Stringha hecho posible con Rust 1.31 cuando const fnse lanzó la función. Desafortunadamente, la única función const que devuelve a Stringes String::new()actualmente, y todavía está detrás de una puerta de función (por lo que se requiere Rust nightly por ahora).

Entonces, el siguiente código hace la conversión deseada (usando todas las noches) ... y en realidad no tiene ningún uso práctico, excepto para mostrar que es posible en este caso de borde.

#![feature(const_string_new)]

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

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

fn main() {
    do_something(&MY_STRING);
}
Zargony
fuente