Почему я не могу вернуть значение & str, полученное из String?

У меня возникают проблемы с попыткой понять, почему я не могу вернуть значение &str, генерируемое из String (доброта, когда будет as_str готова?), и я делаю что-то неправильно. Я получаю эту идею, потому что ничто из того, что я делаю, не заставляет ценность жить достаточно долго, чтобы ее использовать.

Я пытаюсь реализовать error::Error для настраиваемой структуры:

impl error::Error for LexicalError {
    fn description(&self) -> &str {
        let s = format!("{}", self);

        // s doesn't live long enough to do this, I've tried 
        // cloning s and using that, but still the clone doesn't
        // live long enough.
        s.trim()
    }

    fn cause(&self) -> Option<&error::Error> {
        None
    }
}

(для полного фрагмента, вот playpen)

Я не могу понять, как вернуть & str из description, я хотел бы повторно использовать логику Display, если, конечно, я полностью не понимаю, что должно возвращаться description (возможно, краткое описание проблемы). Либо я получаю ту же проблему с возвратом format!(...), которая является переменной, которую я, похоже, не может прожить достаточно долго, чтобы быть полезной для меня.

Ответ 1

Во-первых, давайте взглянем на то, что на самом деле ожидается. В сигнатуре description есть неявное время жизни:

fn description(&self) -> &str
// Can be rewritten as
fn description<'a>(&'a self) -> &'a str

Возвращаемый указатель должен быть действительным, по крайней мере, до тех пор, пока self. Теперь рассмотрим s. Он будет содержать строку String, принадлежащую строке, и она выходит за пределы области действия в конце функции. Было бы неверно возвращать &s, потому что s исчезает, когда функция возвращается. trim возвращает строковый срез, который занимает s, но срез снова действителен только до тех пор, пока s.

Вам нужно вернуть строковый фрагмент, который пережит вызова метода, поэтому это исключает что-либо в стеке. Если бы вы были свободны выбирать тип возврата, решением было бы переместить строку из функции. Для этого потребуется строка, принадлежащая им, а затем возвращаемый тип будет String, а не &str. К сожалению, вы не можете выбрать тип возврата здесь.

Чтобы вернуть фрагмент строки, который переживает вызов метода, я вижу два варианта:

  • Используйте строковый фрагмент &'static. Это, безусловно, переживет вызов, но требует, чтобы строка была известна во время компиляции. Строковые литералы имеют тип &'static str. Это подходящий вариант, если описание не содержит динамических данных.

  • Храните принадлежащую ему строку в LexicalError. Это гарантирует, что вы можете вернуть указатель на него, который действителен для всего срока службы self. Вы можете добавить поле desc: String в LexicalError и выполнить форматирование при построении ошибки. Затем метод будет реализован как

    fn description(&self) -> &str {
        &self.desc
    }
    

    Для повторного использования вы можете сделать Display запись той же строки.

В соответствии с документацией Error, Display может использоваться для дополнительной информации. Если вы хотите включить динамические данные в ошибку, то Display - отличное место для его форматирования, но вы можете опустить его для description. Это позволит использовать первый подход.