Como converter uma string em uma str estática

89

Como faço para converter um Stringem um &str? Mais especificamente, gostaria de convertê-lo em um strcom o statictempo de vida ( &'static str).

Christoph
fonte
Isso não parece possível nem desejável. 'staticO tempo de vida implicaria que a string nunca seria desalocada, ou seja, um vazamento de memória. Por que você precisa em &'static strvez de &'a stralgum apropriado 'a?
3
Como seria convertê-lo em &'a str então?
Christoph
Via as_slice. Seria mais fácil ajudar se você descrevesse o problema concreto que está tentando resolver e os problemas que encontra ao fazer isso.
Observe também SendStrum tipo que é uma string de propriedade ou uma string estática.
Chris Morgan

Respostas:

132

Atualizado para Rust 1.0

Você não pode obter &'static strde um Stringporque Strings podem não durar toda a vida do seu programa, e é isso que &'staticsignifica vida. Você só pode obter uma fatia parametrizada por seu Stringpróprio tempo de vida.

Para ir de uma Stringfatia para uma fatia, &'a strvocê pode usar a sintaxe de fatia:

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

Como alternativa, você pode usar o fato que Stringimplementa Deref<Target=str>e executa um novo empréstimo explícito:

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

Existe ainda outra forma que permite uma sintaxe ainda mais concisa, mas ela só pode ser usada se o compilador for capaz de determinar o tipo de destino desejado (por exemplo, em argumentos de função ou vinculações de variáveis ​​explicitamente digitadas). É chamado de deref coercion e permite usar apenas o &operador, e o compilador irá inserir automaticamente uma quantidade apropriada de *s com base no 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

Observe que este padrão não é exclusivo para String/ &str- você pode usá-lo com todos os pares de tipos que estão conectados por meio de Deref, por exemplo, com CString/ CStre OsString/ OsStrdo std::ffimódulo ou PathBuf/ Pathdo std::pathmódulo.

Vladimir Matveev
fonte
26
Em Rust 1.10, em vez de let s_slice: &str = &s[..];você pode simplesmente fazer isso:let s_slice: &str = s.as_str();
Shnatsel
3
Às vezes, a string original não vive o suficiente, como em um bloco de correspondência {...}. Isso levará a um 's' does not live long enough error.
Dereckson
38

Você pode fazer isso, mas envolve vazar a memória doString . Isso não é algo que você deve fazer levianamente. Ao vazar a memória do String, garantimos que a memória nunca será liberada (daí o vazamento). Portanto, qualquer referência ao objeto interno pode ser interpretada como tendo o 'statictempo de vida.

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
fonte
7
Stringgarante que, enquanto o objeto não for abandonado, a memória permanecerá viva. Uma vez mem::forgetque garante que o objeto nunca será eliminado, temos a garantia de que a referência ao contido strnunca será inválida. Assim, podemos afirmar que é uma 'staticreferência
oli_obk
1
Isso foi incrivelmente útil para o meu aplicativo Rust, que precisava forçar um Stringem um &'static strpara que tokens criados a partir do original Stringestivessem disponíveis em todos os threads. Sem isso, o compilador Rust reclamaria que meu Stringtinha um tempo de vida que terminava no final da função principal, o que não era bom o suficiente porque não tinha a 'staticgarantia.
mmstick
1
@mmstick: a melhor solução nesse caso seria usar crossbeamthreads com escopo
oli_obk
3
@mmstick: se você colocar seu aplicativo inteiro em um escopo de viga cruzada e criar a string fora do escopo, você obterá exatamente isso.
oli_obk
1
Essa resposta é ótima! Ambos me disseram como criar tortuosamente uma fatia de corda estática e me convenceram a não fazer isso! Optei por refatorar meu aplicativo para não usar fatias de string estáticas em tantos lugares.
Paul Chernoch de
21

A partir da versão 1.26 do Rust, é possível converter um Stringpara &'static strsem usar o unsafecódigo:

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

Isso converte a Stringinstância em uma caixa stre vaza imediatamente. Isso libera todo o excesso de capacidade que a string pode ocupar atualmente.

Observe que quase sempre há soluções que são preferíveis a objetos que vazam, por exemplo, usando a crossbeamcaixa se você deseja compartilhar o estado entre os threads.

Sven Marnach
fonte
2

TL; DR: você pode obter um &'static strde um Stringque tem uma 'staticvida inteira.

Embora as outras respostas sejam corretas e mais úteis, há um caso extremo (não tão útil), onde você pode de fato converter a Stringem &'static str:

O tempo de vida de uma referência deve ser sempre menor ou igual ao tempo de vida do objeto referenciado. Ou seja, o objeto referenciado deve viver mais (ou igual) do que a referência. Uma vez que 'staticsignifica toda a vida útil de um programa, não existe uma vida útil mais longa. Mas uma vida útil igual será suficiente. Portanto, se a Stringtiver uma vida útil de 'static, você poderá obter uma &'static strreferência dele.

A criação de um statictipo Stringteoricamente se tornou possível com o Rust 1.31 quando o const fnrecurso foi lançado. Infelizmente, a única função const retornando a Stringé String::new()atualmente, e ainda está atrás de um portão de recurso (então Rust nightly é necessário por enquanto).

Portanto, o código a seguir faz a conversão desejada (usando nightly) ... e, na verdade, não tem uso prático, exceto para a integridade de mostrar que é possível neste caso extremo.

#![feature(const_string_new)]

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

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

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