rust lifetime

rust - Vidas en el óxido



lifetime (1)

De vez en cuando me encuentro con ganas de escribir funciones que se pueden llamar de dos maneras:

// With a string literal: let lines = read_file_lines("data.txt"); // With a string pointer: let file_name = ~"data.txt"; let lines = read_file_lines(file_name);

Mi primera suposición fue usar un puntero prestado ( &str ) para el tipo de parámetro, pero cuando eso no funcionó (solo me permitió usar @str y ~str ), intenté lo siguiente (copiando las bibliotecas de Rust), que funcionó.

fn read_file_lines<''a>(path: &''a str) -> ~[~str] { let read_result = file_reader(~Path(path)); match read_result { Ok(file) => file.read_lines(), Err(e) => fail!(fmt!("Error reading file: %?", e)) } }

El problema es que no entiendo lo que estoy haciendo. De lo que puedo recopilar (principalmente de los errores del compilador), declaro una vida útil en la que no hay restricción, y la uso para describir el parámetro de ruta (lo que significa que se puede pasar cualquier vida como parámetro).

Asi que:

  • ¿Mi entendimiento es vagamente exacto?
  • ¿Qué es una vida? ¿Dónde puedo aprender más sobre ellos?
  • ¿Cuál es la diferencia entre un parámetro de tipo &str y un parámetro de tipo &''a str en el ejemplo anterior?
  • Y mientras estoy en eso, ¿qué es ''self ?

(Estoy usando Rust 0.7, si hace una diferencia en la respuesta)


Actualización 2015-05-16 : el código en la pregunta original se aplicó a una versión anterior de Rust, pero los conceptos siguen siendo los mismos. Esta respuesta se ha actualizado para utilizar las sintaxis / bibliotecas modernas de Rust. (Cambiando esencialmente ~[] a Vec y ~str a String y ajustando el ejemplo de código al final.)

¿Mi entendimiento es vagamente exacto?
[...]
¿Cuál es la diferencia entre un parámetro de tipo & str y un parámetro de tipo & ''a str en el ejemplo anterior?

Sí, una vida así dice esencialmente "sin restricciones", más o menos. Las vidas útiles son una forma de conectar valores de salida con entradas, es decir, fn foo<''a, T>(t: &''a T) -> &''a T dice que foo devuelve un puntero que tiene la misma duración que t , es decir , los datos a los que apunta son válidos durante el mismo período de tiempo que t (bueno, estrictamente, al menos tanto como). Esto básicamente implica que el valor de retorno apunta a alguna subsección de la memoria que t apunta.

Entonces, una función como fn<''a>(path: &''a str) -> Vec<String> es muy similar a escribir { let x = 1; return 2; } { let x = 1; return 2; } { let x = 1; return 2; } ... es una variable no utilizada.

Rust asigna &str vida predeterminados al escribir &str , y esto es exactamente equivalente a escribir el tiempo de vida de la variable no utilizada. es decir, fn(path: &str) -> Vec<String> no es diferente de la versión con ''a s. La única vez que se deja una vida útil es diferente a incluirlo si necesita aplicar un puntero global (es decir, la vida útil ''static especial), o si desea devolver una referencia (por ejemplo, -> &str ) que solo es posible si el valor de retorno tiene un tiempo de vida (y este debe ser el tiempo de vida de una o más entradas, o ''static ).

¿Qué es una vida? ¿Dónde puedo aprender más sobre ellos?

Una vida útil es el tiempo que se garantiza que existan los datos a los que apunta un puntero, por ejemplo, una variable global tiene la garantía de durar "para siempre" (por lo que tiene la vida útil especial ''static ). Una buena forma de verlos es: la vida útil conecta los datos al marco de pila en el que se encuentra su propietario; una vez que el marco de la pila se cierra, el propietario queda fuera del alcance y los punteros a / en ese valor / estructura de datos ya no son válidos, y la vida útil es una manera para que el compilador pueda razonar al respecto. (Con la vista de marco de pila, es como si @ tuviera un marco de pila especial asociado con la tarea actual, y las static tienen un marco de pila "global").

También hay un capítulo del libro sobre tiempos de vida útil , y este resumen (NB. El código ahora está desactualizado pero los conceptos siguen siendo ciertos) es una pequeña demostración de cómo se pueden usar los tiempos de vida para evitar tener que copiar / asignar (con una gran seguridad Garantía: no hay posibilidad de punteros colgantes).

Y mientras estoy en eso, ¿qué es ''self ?

Literalmente, nada especial, solo ciertos lugares requieren que los tipos tengan vidas (por ejemplo, en las definiciones de struct / enum e in impl s), y actualmente ''self ''static y ''static son los únicos nombres aceptados. ''static para punteros globales siempre válidos, ''self para algo que puede tener una vida útil. Es un error que llamar a esa vida (no static ) otra cosa que no sea self es un error.

En general, escribiría esa función como:

use std::fs::File; use std::io::prelude::*; use std::io::BufReader; use std::path::Path; fn read_file_lines(path: &Path) -> Vec<String> { match File::open(path) { Ok(file) => { let read = BufReader::new(file); read.lines().map(|x| x.unwrap()).collect() } Err(e) => panic!("Error reading file: {}", e) } } fn main() { let lines = read_file_lines(Path::new("foo/bar.txt")); // do things with lines }