Capire le vite: la durata massima e 'statica

Sep 16 2020

Il mio processo di apprendimento per la durata della ruggine era simile a questo (basato sul libro sulla ruggine):

  1. Voglio annotare, quando i valori dietro i riferimenti escono dall'ambito
  2. Di solito (non sempre! Vedere la sezione .data, cioè 'statico) i valori vivono all'interno di un {}blocco
  3. Annotiamo blocchi come 't: {…}e, ad esempio, i campi struct ottengono una durata simile &'t identcon lo stesso nome di duratat
  4. Questa comprensione è sbagliata. Perché? Le definizioni del nome del blocco sono molto probabilmente sconosciute all'implementatore della struttura e potrebbero esserci diverse definizioni del nome del blocco per la stessa struttura.
  5. Quindi la definizione 't: {…}e l'uso &'t identdevono essere completamente indipendenti.
  6. I compilatori possono facilmente determinare le definizioni, quindi gli utenti non devono mai scrivere 't: {…}. I programmatori devono preoccuparsi solo della &'t identparte delle specifiche.
  7. I compilatori potrebbero analizzare i corpi delle funzioni (in caso di struct: uso dei membri della struttura) e determinare la &'t identparte.
  8. Questa comprensione è sbagliata. Perché? Perché a volte il corpo della funzione (o l'uso dei membri della struttura) non è ancora disponibile (ad esempio, un tratto specifica una funzione, ma l'implementazione viene eseguita da un'altra parte in futuro).
  9. Di conseguenza, structe fndevono specificare completamente le durate rispettivamente nella definizione della struttura o nella firma della funzione.
  10. Le specifiche seguono principalmente le stesse regole euristiche. Quindi introduciamo l'elisione a vita. Inserisce le durate in base a regole mirate ai casi di utilizzo più comuni e possiamo disattivarli in qualsiasi momento.

A questo punto, penso che la mia comprensione sia abbastanza vicina a come funziona effettivamente. Ma ora, la mia comprensione è sbagliata. Vediamo qualche esempio:

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

Ovviamente, ho omesso qualsiasi specifica di durata.

  • La durata dei campi struct è l'intera durata del programma ( 'static) o fintanto che la struttura ( Stats<'a>con league: &'a str)

  • In una funzione / metodo, potremmo avere riferimenti con tempi di vita 'a, 'b, 'c, .... Qual è la durata del valore restituito?

    • O è un valore statico ( 'static)
    • O è sempre la stessa durata specifica (come 'c)
    • O è una durata specifica, quale sarà nota in fase di compilazione o esecuzione. Per il compilatore dobbiamo specificare la durata del caso peggiore max('a, 'b, 'c, …). Per quanto ne so, questo può essere fatto assegnando a ogni riferimento la stessa durata.

Questo sembra funzionare per la seguente funzione artificiosa e più breve:

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

Se aggiungiamo un 'staticvalore di ritorno, la durata del caso peggiore è max('a, 'static)che presumibilmente è '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++"
  }
}

Questo dà error[E0621]: explicit lifetime required in the type of s1e lifetime 'static requiredper s2.league.

A che punto la mia comprensione è sbagliata? Grazie in anticipo per avermi sopportato.

Disclaimer: help: add explicit lifetime 'static to the type of s1: &'a Stats<'static> funzionerebbe qui, ma mi sembra sbagliato.

Risposte

2 prog-fh Sep 16 2020 at 09:01

Modificherei il tuo codice come fornito di seguito.

Invece di fingere che il risultato di more_difficult_league()abbia una durata statica (che non è il caso quando ci riferiamo a s1o s2, e il compilatore se ne lamenta), possiamo introdurre una nuova annotazione della durata per questo risultato e specificare che la durata dei parametri deve sopravvivere a questo risultato (la whereclausola).

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