Anotações de Tempo de Vida
Uma referência tem um tempo de vida (lifetime), que não deve "sobreviver" ao valor ao qual se refere. Isso é verificado pelo borrow checker (verificador de empréstimo).
O tempo de vida pode ser implícito - isso é o que vimos até agora. Os tempos de vida também podem ser explícitos: &'a Point
, &'document str
. Os tempos de vida começam com '
e 'a
é um nome padrão típico. Leia &'a Point
como "um Point
emprestado que é válido por pelo menos o tempo de vida a
".
Os tempos de vida são sempre inferidos pelo compilador: você não pode atribuir um tempo de vida por conta própria. Anotações explícitas de tempo de vida criam restrições onde há ambiguidade; o compilador verifica se há uma solução válida.
Os tempos de vida se tornam mais complicados ao considerar a passagem de valores para e a devolução de valores de funções.
#[derive(Debug)] struct Point(i32, i32); fn left_most(p1: &Point, p2: &Point) -> &Point { if p1.0 < p2.0 { p1 } else { p2 } } fn main() { let p1: Point = Point(10, 10); let p2: Point = Point(20, 20); let p3 = left_most(&p1, &p2); // Qual é o tempo de vida de p3? println!("p3: {p3:?}"); }
Neste exemplo, o compilador não sabe qual tempo de vida inferir para p3
. Olhando dentro do corpo da função nos mostra que ele só pode assumir com segurança que o tempo de vida de p3
é o mais curto de p1
e p2
. Mas assim como os tipos, o Rust requer anotações explícitas de tempos de vida nos argumentos e valores de retorno da função.
Adicione 'a
apropriadamente a left_most
(mais à esquerda):
fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {
Isto diz, "dado p1 e p2 que sobrevivem a 'a
, o valor de retorno vive por pelo menos 'a
.
Em casos comuns, os tempos de vida podem ser omitidos, como descrito no próximo slide.