Noções básicas sobre tempos de vida: vida máxima e 'estático
Meu processo de aprendizagem para a vida útil da ferrugem era assim (baseado no livro sobre ferrugem):
- Eu quero anotar, quando os valores atrás das referências saem do escopo
- Normalmente (nem sempre! Consulte a seção .data, ou seja, 'estático), os valores residem em um
{}bloco - Nós anotamos blocos como
't: {…}e, por exemplo, campos de estrutura obtêm um tempo de vida&'t identcom o mesmo nome de tempo de vidat - Esse entendimento está errado. Por quê? As definições de nome de bloco são provavelmente desconhecidas para o implementador de estrutura e pode haver várias definições de nome de bloco para a mesma estrutura.
- Portanto, a definição
't: {…}e o uso&'t identdevem ser completamente independentes. - Os compiladores podem determinar facilmente as definições, portanto os usuários nunca precisam escrever
't: {…}. Os programadores só precisam se preocupar com a&'t identparte da especificação. - Os compiladores podem analisar corpos de função (no caso de
struct: uso dos membros da estrutura) e determinar a&'t identparte. - Esse entendimento está errado. Por quê? Porque às vezes o corpo da função (ou uso de membros de estrutura) ainda não está disponível (por exemplo, um traço especifica uma função, mas a implementação é feita por outra parte no futuro).
- Como resultado,
structefndeve especificar totalmente os tempos de vida em sua definição de estrutura ou assinatura de função, respectivamente. - As especificações geralmente seguem as mesmas regras heurísticas. Portanto, introduzimos a elisão vitalícia. Ele insere vidas úteis com base em regras que visam os casos de uso mais comuns e podemos cancelar a qualquer momento.
Neste ponto, acho que meu entendimento está bem próximo de como isso realmente funciona. Mas agora, meu entendimento está errado. Vejamos alguns exemplos:
#[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, omiti todas as especificações vitalícias.
O tempo de vida dos campos de estrutura é toda a duração do programa (
'static) ou enquanto a estrutura (Stats<'a>comleague: &'a str)Em uma função / método, podemos obter referências com vidas
'a,'b,'c, .... Qual é a vida útil do valor de retorno?- Ou é algum valor estático (
'static) - Ou é sempre a mesma vida útil específica (como
'c) - Ou é um tempo de vida específico - qual será conhecido em tempo de compilação ou execução. Para o compilador, devemos especificar o pior caso de vida
max('a, 'b, 'c, …). Até onde sei, isso pode ser feito dando a cada referência a mesma duração.
- Ou é algum valor estático (
Isso parece funcionar para a seguinte função artificial e mais curta:
fn more_difficult_league<'a>(s1: &'a Stats, s2: &'a Stats) -> &'a str {
if s1.league == s2.league {
s1.league
} else {
s2.league
}
}
Se adicionarmos algum 'staticvalor de retorno, o pior caso de vida max('a, 'static)é, presumivelmente '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++"
}
}
Isso dá error[E0621]: explicit lifetime required in the type of s1e lifetime 'static requiredpara s2.league.
Em que ponto meu entendimento está errado? Agradeço antecipadamente por ter paciência comigo.
Aviso: help: add explicit lifetime 'static to the type of s1: &'a Stats<'static> funcionaria aqui, mas parece errado para mim.
Respostas
Eu mudaria seu código conforme fornecido abaixo.
Em vez de fingir que o resultado de more_difficult_league()tem um tempo de vida estático (o que não é o caso quando nos referimos a s1ou s2, e o compilador reclama sobre isso), podemos introduzir uma nova anotação de tempo de vida para esse resultado e especificar que os tempos de vida dos parâmetros devem sobreviver a esse resultado (a 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);
}