Jak przekonwertować ciąg znaków na statyczny ciąg & '

92

Jak zamienić a Stringna &str? Dokładniej, chciałbym zamienić go na a strz staticlifetime ( &'static str).

Christoph
źródło
To nie wydaje się możliwe ani pożądane. 'staticżywotność oznaczałaby, że ciąg nigdy nie zostałby cofnięty, tj. wyciek pamięci. Dlaczego potrzebujesz &'static strzamiast &'a strjakiegoś odpowiedniego 'a?
3
Jak by to wyglądało, gdyby to przekształcić w &'a str to?
Christoph
Via as_slice. Byłoby łatwiej, gdybyś opisał, jaki konkretny problem próbujesz rozwiązać i jakie problemy napotykasz przy tym.
Zwróć również uwagę SendStrna typ, który jest ciągiem będącym własnością lub ciągiem statycznym.
Chris Morgan

Odpowiedzi:

135

Zaktualizowano dla Rust 1.0

Nie możesz uzyskać &'static strod a, Stringponieważ Strings mogą nie żyć przez całe życie twojego programu i to właśnie &'staticoznacza życie. Możesz uzyskać z niego tylko wycinek sparametryzowany przez Stringwłasny okres istnienia.

Aby przejść od a Stringdo plasterka &'a str, możesz użyć składni krojenia:

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

Alternatywnie możesz użyć faktu, że Stringimplementuje Deref<Target=str>i przeprowadza jawne ponowne pożyczanie:

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

Istnieje nawet inny sposób, który pozwala na jeszcze bardziej zwięzłą składnię, ale można go użyć tylko wtedy, gdy kompilator jest w stanie określić żądany typ docelowy (np. W argumentach funkcji lub jawnie wpisanych wiązaniach zmiennych). Nazywa się to deref coercion i pozwala na użycie tylko &operatora, a kompilator automatycznie wstawi odpowiednią ilość *s na podstawie kontekstu:

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

Zauważ, że ten wzorzec nie jest unikalny dla String/ &str- możesz go użyć z każdą parą typów, które są połączone Deref, na przykład z CString/ CStri OsString/ OsStrz std::ffimodułu lub PathBuf/ Pathz std::pathmodułu.

Vladimir Matveev
źródło
29
W Rust 1.10 zamiast tego let s_slice: &str = &s[..];możesz po prostu zrobić to:let s_slice: &str = s.as_str();
Shnatsel
3
Czasami oryginalny ciąg nie jest wystarczająco żywy, jak w bloku dopasowania {...}. Doprowadzi to do 's' does not live long enough error.
Dereckson
41

Możesz to zrobić, ale wiąże się to z wyciekiem pamięciString . Nie należy tego robić lekko. Wyciekając z pamięci String, gwarantujemy, że pamięć ta nigdy nie zostanie uwolniona (a tym samym wyciek). Dlatego wszelkie odwołania do obiektu wewnętrznego mogą być interpretowane jako mające 'staticokres istnienia.

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
źródło
8
Stringdaje gwarancję, że dopóki obiekt nie zostanie upuszczony, pamięć pozostaje żywa. Ponieważ mem::forgetgwarantujemy, że obiekt nigdy nie zostanie porzucony, mamy gwarancję, że odwołanie do zawartego w nim strnigdy nie będzie nieprawidłowe. W ten sposób możemy stwierdzić, że jest to 'staticodniesienie
oli_obk
1
Było to niezwykle pomocne dla mojej aplikacji Rust, która musiała przekształcić a Stringw a &'static str, aby tokeny utworzone na podstawie oryginału Stringbyły dostępne we wszystkich wątkach. Bez tego kompilator Rust narzekałby, że moje Stringżycie kończyło się wraz z końcem głównej funkcji, co nie było wystarczająco dobre, ponieważ nie miało 'staticgwarancji.
mmstick
1
@mmstick: lepszym rozwiązaniem w takim przypadku byłoby użycie crossbeamwątków o określonym zakresie
oli_obk
3
@mmstick: jeśli umieścisz całą aplikację w zasięgu belki poprzecznej i utworzysz ciąg poza zasięgiem, otrzymasz dokładnie to.
oli_obk
1
Ta odpowiedź jest świetna! Powiedział mi, jak przebiegło stworzyć statyczny kawałek struny i przekonał mnie, żebym tego nie robił! Zdecydowałem się zreformować moją aplikację, aby nie używać statycznych wycinków ciągów w tak wielu miejscach.
Paul Chernoch,
23

Od wersji Rust 1.26 możliwe jest przekonwertowanie pliku Stringna &'static strbez użycia unsafekodu:

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

To przekształca Stringinstancję w pudełkową stri natychmiast ją przecieka. To uwalnia całą nadmiarową pojemność, jaką struna może obecnie zajmować.

Zauważ, że prawie zawsze istnieją rozwiązania, które są lepsze niż przeciekające obiekty, np. Użycie crossbeamskrzynki, jeśli chcesz podzielić stan między wątkami.

Sven Marnach
źródło
2

TL; DR: możesz dostać &'static strod, Stringktóry sam ma 'staticcałe życie.

Chociaż inne odpowiedzi są poprawne i najbardziej przydatne, istnieje (niezbyt przydatny) przypadek skrajny, w którym rzeczywiście można przekonwertować a Stringna &'static str:

Okres istnienia odwołania musi zawsze być krótszy lub równy okresowi istnienia obiektu, do którego się odwołuje. Oznacza to, że obiekt, do którego odwołuje się odwołanie, musi żyć dłużej (lub tak samo długo) niż odniesienie. Ponieważ 'staticoznacza cały czas trwania programu, dłuższe życie nie istnieje. Ale wystarczy równe życie. Więc jeśli Stringma żywotność 'static, możesz uzyskać&'static str z niego odniesienie.

Tworzenie statictypu Stringteoretycznie stało się możliwe w Rust 1.31, kiedy ta const fnfunkcja została wydana. Niestety jedyną funkcją const zwracającą a StringjestString::new() obecnie i nadal znajduje się za bramą funkcji (więc na razie wymagana jest nocna funkcja Rust).

Tak więc poniższy kod wykonuje żądaną konwersję (używając nightly) ... i tak naprawdę nie ma praktycznego zastosowania z wyjątkiem kompletnego pokazania, że ​​jest to możliwe w tym przypadku skrajnym.

#![feature(const_string_new)]

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

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

fn main() {
    do_something(&MY_STRING);
}
Zargony
źródło