Comprender la vida útil: vida útil máxima y 'estática

Sep 16 2020

Mi proceso de aprendizaje para la vida útil del óxido se ve así (basado en el libro de óxido):

  1. Quiero hacer anotaciones, cuando los valores detrás de las referencias se salen del alcance
  2. Por lo general (¡no siempre! Vea la sección .data, es decir, los valores 'estáticos) viven dentro de un {}bloque
  3. Anotamos bloques como 't: {…}y, por ejemplo, los campos de estructura obtienen un estilo de por vida &'t identcon el mismo nombre de por vidat
  4. Esta comprensión está mal. ¿Por qué? Las definiciones de nombre de bloque probablemente sean desconocidas para el implementador de la estructura y puede haber varias definiciones de nombre de bloque para la misma estructura.
  5. Por tanto, la definición 't: {…}y el uso &'t identdeben ser completamente independientes.
  6. Los compiladores pueden determinar fácilmente las definiciones, por lo que los usuarios nunca tienen que escribir 't: {…}. Los programadores solo deben preocuparse por la &'t identparte de especificación.
  7. Los compiladores podrían analizar los cuerpos de las funciones (en caso de struct: uso de los miembros de la estructura) y determinar la &'t identparte.
  8. Esta comprensión es incorrecta. ¿Por qué? Porque a veces el cuerpo de la función (o el uso de miembros de estructura) aún no está disponible (por ejemplo, un rasgo especifica una función, pero la implementación la realiza otra parte en el futuro).
  9. Como resultado, structy fndebe especificar completamente la vida útil en su definición de estructura o firma de función, respectivamente.
  10. Las especificaciones siguen en su mayoría las mismas reglas heurísticas. Así que presentamos la elisión de por vida. Inserta vidas útiles basadas en reglas dirigidas a los casos de uso más comunes y podemos optar por no participar en cualquier momento.

En este punto, creo que mi comprensión está bastante cerca de cómo funciona realmente. Pero ahora, mi comprensión se equivoca. Veamos algún ejemplo:

#[derive(Debug)]
struct Stats {
  league: &str,
}

const NAME: &str = "rust";

fn more_difficult_league(s1: &Stats, s2: &Stats) -> &str {
  if s1.league == s2.league {
    s1.league
  } else if s1.league == "PHP" {
    s2.league
  } else {
    "C++"
  }
}


fn main() {
  let mut st = Stats { league: name };
  let dleague = more_difficult_league(&st, &st);
  println!("{}", dleague);
}

Obviamente, omití las especificaciones de por vida.

  • La vida útil de los campos de estructura es la duración completa del programa ( 'static) o siempre que la estructura ( Stats<'a>con league: &'a str)

  • En una función / método, podríamos obtener referencias con tiempos de vida 'a, 'b, 'c, .... ¿Cuál es la vida útil del valor de retorno?

    • O es algún valor estático ( 'static)
    • O siempre es la misma vida específica (como 'c)
    • O es una vida útil específica, cuál se conocerá en el tiempo de compilación o ejecución. Para el compilador, debemos especificar el peor tiempo de vida max('a, 'b, 'c, …). Hasta donde yo sé, esto se puede hacer dando a cada referencia la misma duración.

Esto parece funcionar para la siguiente función artificial más corta:

fn more_difficult_league<'a>(s1: &'a Stats, s2: &'a Stats) -> &'a str {
  if s1.league == s2.league {
    s1.league
  } else {
    s2.league
  }
}

Si agregamos algún 'staticvalor de retorno, el peor tiempo de vida es el max('a, 'static)que presumiblemente es 'static:

fn more_difficult_league<'a>(s1: &'a Stats, s2: &'a Stats) -> &'static str {
  if s1.league == s2.league {
    s1.league
  } else if s1.league == "PHP" {
    s2.league
  } else {
    "C++"
  }
}

Esto da error[E0621]: explicit lifetime required in the type of s1y lifetime 'static requiredpara s2.league.

¿En qué punto está mal entendido? Gracias de antemano por soportarme.

Descargo de responsabilidad: help: add explicit lifetime 'static to the type of s1: &'a Stats<'static> funcionaría aquí, pero me parece incorrecto.

Respuestas

2 prog-fh Sep 16 2020 at 09:01

Cambiaría su código como se indica a continuación.

En lugar de pretender que el resultado de more_difficult_league()tiene una vida útil estática (que no es el caso cuando nos referimos a s1o s2, y el compilador se queja de eso), podemos introducir una nueva anotación de vida útil para este resultado y especificar que la vida útil de los parámetros debe sobrevivir a este resultado (la wherecláusula).

#[derive(Debug)]
struct Stats<'a> {
    league: &'a str,
}

const NAME: &str = "rust";

fn more_difficult_league<'a, 'b, 'c>(
    s1: &'a Stats,
    s2: &'b Stats,
) -> &'c str
where
    'a: 'c,
    'b: 'c,
{
    if s1.league == s2.league {
        s1.league
    } else if s1.league == "PHP" {
        s2.league
    } else {
        "C++"
    }
}

fn main() {
    let st = Stats { league: NAME };
    let dleague = more_difficult_league(&st, &st);
    println!("{}", dleague);
}