Почему эта функция требует времени жизни, если все аргументы реализуют свойство копирования?

В следующем коде я не понимаю, почему для Span требуется время жизни. Поскольку все аргументы реализуют свойство копирования, я понимаю, что Span владеет всеми необходимыми переменными.

use ratatui::{
    style::{Color, Style},
    text::Span,
};

/// Take a u8, and render a colorized ascii, or placeholdler
fn render_ascii_char(val: u8) -> Span {
    match val {

        val if val > 0x20 && val < 0x7f => {
            Span::styled(
                val.to_string(),
                Style::default().fg(Color::LightCyan)
            )
        },

        _ => {
            Span::styled(
                "•",
                Style::default().fg(Color::Yellow)
            )
        }
    }
}

fn test_function() -> Span {
    let val = 0x42;

    render_ascii_char(val)
}

fn main() {
    let _my_span = test_function();
}

Используйте этот код ratatui. Ниже Cargo.toml:

[package]
name = "span_lifetime"
version = "0.1.0"
edition = "2024"

[dependencies]
crossterm = "0.27.0"
ratatui = "0.26.2"

При компиляции с cargo build у меня появляется следующее сообщение об ошибке:

$ cargo build
   Compiling span_lifetime v0.1.0 (/tmp/playground/span_lifetime)
error[E0106]: missing lifetime specifier
 --> src/main.rs:7:34
  |
7 | fn render_ascii_char(val: u8) -> Span {
  |                                  ^^^^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
  |
7 | fn render_ascii_char(val: u8) -> Span<'static> {
  |                                      +++++++++

error[E0106]: missing lifetime specifier
  --> src/main.rs:26:23
   |
26 | fn test_function() -> Span {
   |                       ^^^^ expected named lifetime parameter
   |
   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`, or if you will only have owned values
   |
26 | fn test_function() -> Span<'static> {
   |                           +++++++++

For more information about this error, try `rustc --explain E0106`.
error: could not compile `span_lifetime` (bin "span_lifetime") due to 2 previous errors

Использование времени жизни <'static> не кажется хорошей идеей, поскольку я не использую эту функцию только с константами.

Использование ссылки val: &u8 в качестве аргумента вместо val: u8 работает. Но это создает проблемы, когда переданное значение выходит за рамки.

Мое решение до сих пор состоит в том, чтобы указать время жизни:

fn render_ascii_char<'a>(val: u8) -> Span<'a> {
    match val {
[...]

Но я действительно не понимаю, почему это необходимо и какие последствия это может иметь в этой жизни.

Почему Python в конце концов умрет
Почему Python в конце концов умрет
Последние 20 лет были действительно хорошими для Python. Он прошел путь от "просто языка сценариев" до основного языка, используемого для написания...
0
0
70
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Из определения Пролета:

pub struct Span<'a> {
    pub content: Cow<'a, str>,
    pub style: Style,
}

Вы можете видеть, что он содержит Корову , которая может содержать ссылку в варианте Заимствованный:

pub enum Cow<'a, B>
where
    B: 'a + ToOwned + ?Sized,
{
    Borrowed(&'a B),
    Owned(<B as ToOwned>::Owned),
}

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

Поскольку вы не выполняете ни одного из условий, при которых время жизни может быть исключено, вам необходимо явно добавить его.

'static — это правильное время жизни, которое следует использовать здесь, это вовсе не означает, что вы используете или можете использовать только возвращаемый Span с константами, но вместо этого он не содержит ссылок, которые живут короче, чем 'static.

Насколько я понимаю, использование 'static делает переменную активной на время работы программы (doc.rust-lang.org/rust-by-example/scope/lifetime/…). Поскольку эта функция часто используется в моем коде, кажется, что использование 'static приведет к утечке памяти, когда я всегда выделяю память, которая никогда не освобождается. Или я здесь что-то не понимаю?

Olivier Lasne 02.06.2024 11:19

Хорошо, я понимаю, что использование 'static означает, что мы указываем на некую память, которая всегда действительна. Либо константа (здесь наш '•'), либо собственное значение. Поскольку val копируется при передаче в функцию, это собственное значение. Итак, 'static — правильный образ действий. Буду рад, если кто-нибудь это подтвердит

Olivier Lasne 02.06.2024 11:38
'static здесь не имеет никакого эффекта для варианта Cow::Owned (String). Он используется только в Cow::Borrowed, и в этом случае да, указанная память должна жить до конца программы, но это тривиально верно для строковых литералов, таких как "•", они компилируются в исполняемый файл. Аннотация времени жизни (даже 'static) сама по себе не может создать утечку памяти, это может сделать только явный метод, такой как Box::leak. Однако это распространенное заблуждение всей жизни
cafce25 02.06.2024 12:17

Другие вопросы по теме