Як перетворити рядок у & 'статичний str


91

Як перетворити a Stringна a &str? Більш конкретно, я хотів би перетворити його на a strз staticжиттям ( &'static str).


Це не здається можливим чи бажаним. 'staticпротягом життя означатиме, що рядок ніколи не буде звільнено, тобто витік пам'яті. Навіщо вам &'static strзамість &'a strякогось відповідного 'a?

3
Як тоді виглядатиме його перетворення &'a str ?
Крістоф

Через as_slice. Було б простіше допомогти, якби ви описали, яку конкретну проблему ви намагаєтесь вирішити та з якими проблемами стикаєтесь при цьому.

Також зверніть увагу SendStr, тип, який є або належить рядком, або статичним рядком.
Chris Morgan

Відповіді:


134

Оновлено для Rust 1.0

Ви не можете отримати &'static strз a, Stringтому що Strings може не прожити протягом усього життя вашої програми, і ось що &'staticозначає життя. Ви можете отримати фрагмент, параметризований за Stringвласним часом життя, з нього.

Щоб перейти від Stringфрагмента до фрагмента, &'a strви можете використовувати синтаксис нарізки:

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

Крім того, ви можете використовувати той факт, що Stringреалізує Deref<Target=str>та виконує явне перезавантаження:

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

Існує навіть інший спосіб, який дозволяє отримати ще більш стислий синтаксис, але він може бути використаний лише в тому випадку, якщо компілятор може визначити бажаний тип цілі (наприклад, у аргументах функції або явно набраних прив'язках змінних). Це називається примусом deref, і воно дозволяє використовувати лише &оператор, і компілятор автоматично вставить відповідну кількість *s на основі контексту:

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

Зверніть увагу, що цей шаблон не є унікальним для String/ &str- ви можете використовувати його з кожною парою типів, які з'єднані Deref, наприклад, з CString/ CStrта OsString/ OsStrз std::ffiмодуля або PathBuf/ Pathз std::pathмодуля.


29
У Rust 1.10 замість цього let s_slice: &str = &s[..];ви можете просто зробити це:let s_slice: &str = s.as_str();
Шнацель

3
Іноді оригінальний рядок живе недостатньо, як у блоці збігу {...}. Це призведе до 's' does not live long enough error.
Дерексон,

41

Ви можете це зробити, але це передбачає витік пам'ятіString . Це не те, що ви повинні робити легковажно. Витікаючи з пам'яті String, ми гарантуємо, що пам'ять ніколи не звільниться (отже, витік). Тому будь-які посилання на внутрішній об'єкт можна інтерпретувати як такі, що мають час 'staticжиття.

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);
}

8
Stringгарантує, що до тих пір, поки предмет не буде скинуто, пам'ять залишатиметься живою. Оскільки mem::forgetгарантується, що об’єкт ніколи не буде скинуто, ми гарантуємо, що посилання на вміщений strніколи не буде недійсним. Таким чином, ми можемо стверджувати, що це 'staticпосилання
oli_obk

1
Це було неймовірно корисно для мого додатку Rust, якому потрібно було примусити Stringa, &'static strщоб маркери, створені з оригіналу, Stringбули доступні у всіх потоках. Без цього компілятор Rust скаржився б на те, що у мене Stringбуло все життя, яке закінчилося в кінці основної функції, що було недостатньо добре, оскільки він не мав 'staticгарантії.
mmstick

1
@mmstick: найкращим рішенням у такому випадку було б використовувати crossbeamнитки та
сфери дії

3
@mmstick: якщо ви покладете всю свою програму на перехресну область і створите рядок поза сферою, ви отримаєте саме це.
oli_obk

1
Ця відповідь чудова! Це обоє розповіло мені, як хитро створювати статичний фрагмент рядка, і переконало мене цього не робити! Я вибрав рефакторинг мого додатка, щоб не використовувати статичні фрагменти рядків у стільки місцях.
Пол Черноч,

22

За Rust версії 1.26, можна перетворити Stringв &'static strбез використання unsafeкоду:

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

Це перетворює Stringекземпляр у коробку strта негайно витікає з нього. Це звільняє всю зайву ємність, яку зараз може займати рядок.

Зверніть увагу, що майже завжди є рішення, які є кращими, ніж об'єкти, що витікають, наприклад, використання crossbeamобрешітки, якщо ви хочете поділити стан між потоками.


2

TL; DR: ви можете отримати &'static stra, Stringякий сам має 'staticвсе життя.

Хоча інші відповіді є правильними та найбільш корисними, є (не дуже корисний) крайній випадок, де ви дійсно можете перетворити a Stringна a &'static str:

Час життя посилання завжди має бути коротшим або рівним часу життя об’єкта, на який посилається. Тобто об'єкт, на який посилається, повинен жити довше (або рівно довше), ніж посилання. Оскільки 'staticозначає весь термін служби програми, більш тривалого життя не існує. Але рівного терміну життя буде цілком достатньо. Отже, якщо а Stringмає тривалість життя 'static, ви можете отримати &'static strдовідку з нього.

Створення staticтипу Stringстало теоретично можливим завдяки Rust 1.31, коли ця const fnфункція була випущена. На жаль, на даний момент єдиною функцією const, яка повертає a, Stringє String::new(), і вона все ще перебуває за функціональними воротами (тому наразі потрібно Rust nightly).

Отже, наступний код робить бажане перетворення (за допомогою щоночі) ... і насправді не має практичного використання, за винятком повноти показу, що це можливо в цьому крайньому випадку.

#![feature(const_string_new)]

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

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

fn main() {
    do_something(&MY_STRING);
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.