v1801 servidores servers original online lista experimental rust lifetime subtyping

servidores - servers de rust alpha



Luchando con la relaciĆ³n de subtipos de vidas en Rust (1)

Descargo de responsabilidad: no soy exactamente un gurú de la sociedad civil, por lo que esta respuesta se centrará en los conceptos prácticos y ni siquiera intentaré vincularlos con los conceptos teóricos para no hacer un lío de cosas.

Creo que el problema es tratar de aplicar el concepto de subtipo a algo que no es un tipo.

  • ''a es una vida
  • &''a T es un tipo

Puede comparar &''a T y &''b U y ver si obedecen a una relación de subtipo, pero no puede establecer una relación de subtipo con dos vidas en el resumen porque:

  • a veces, para ser sustituibles, la nueva vida útil debe ser mayor que la vida útil reemplazada.
  • a veces, para ser sustituibles, la nueva vida útil debe ser más pequeña que la vida reemplazada.

Podemos verificar esto a través de dos simples ejemplos.

El primer ejemplo es quizás el más fácil: ¡una vida puede ser sustituida si es mayor!

// Using a lifetime as a bound struct Reference<''a, T> where T: ''a { data: &''a T } fn switch<''a, ''b, T>(r: &mut Reference<''a, T>, new: &''b T) where ''b: ''a { r.data = new; }

Aquí, el compilador solo permite la sustitución si ''b es al menos tan grande como ''a que se expresa mediante el límite de por vida ''b: ''a . Esto se debe a que Rust aborrece las referencias colgantes y, por lo tanto, un contenedor solo puede contener referencias a objetos que lo sobrevivan.

Cuando se utiliza como garantía , una vida útil más larga es un subtipo de una vida útil menor y se puede sustituir en su lugar. Esto sugiere como lo menciona @aturon, que en este uso ''static es un subtipo de todas las vidas.

El segundo ejemplo es un poco más complicado: ¡una vida puede ser sustituida si es menor!

Comencemos con lo siguiente:

struct Token; fn restrict<''a, ''b, T>(original: &''a T, _: &''b Token) -> &''b T where ''a: ''b { original }

El siguiente uso es correcto:

fn main() { let i = 4; { let lesser = Token; let k = restrict(&i, &lesser); println!("{}", k); } }

Y nuestra demostración anterior dijo que podemos sustituir una vida más larga en lugar de una menor:

fn main() { let greater = Token; let j; // prevent unification of lifetimes { let i = 4; j = restrict(&i, &greater); } println!("{}", j); } error: `i` does not live long enough j = restrict(&i, &greater);

Cuando se utiliza como una restricción , una vida útil menor es un subtipo de una vida útil mayor y se puede sustituir en su lugar. En este uso, ''static es un supertipo de todas las vidas.

Por lo tanto, no existe una única relación de subtipo entre tiempos de vida porque cumplen dos propósitos radicalmente opuestos.

Recapitular:

  • cuando se utiliza como garantía : greater <: lesser
  • cuando se usa como una restricción : lesser <: greater

Nota: una vida útil puede actuar de forma plausible tanto como una garantía Y una restricción al mismo tiempo.

Me siento tonto por haber navegado la sección de marcadores de la documentación de Rust y los artículos de Wikipedia sobre subtyping y variance varias veces sin que mejore mi comprensión de la relación de subtipos de vida útil.

Creo que estoy acostumbrado a las relaciones de subtipo "OOP-style" típicas como "Cat <: Animal" que significa "Cat es un subtipo de Animal" donde "S es un subtipo de T" significa "cualquier término S puede ser seguro usado en un contexto donde se espera un término de tipo T ". Hasta aquí todo bien.

Pero, ¿cómo se aplica esto a las vidas? La forma en que se define en este momento en Rust es aparentemente (*)

(# 1) ''a <:'' b <=> el tiempo de vida a no es más que el tiempo de vida b.

Y podrías pensar "¡Por supuesto que eso es lo que significa!" posiblemente porque <: se parece al operador less than o posiblemente porque "sub" le hace pensar en subconjuntos y una vida útil más corta es ciertamente un subconjunto de una vida útil más larga. Pero, ¿es ''realmente un subtipo de'' b si ''a no es más largo que'' b? Intentemos aplicar la definición de Wikipedia de la relación de subtipo:

(# 2) ''a <:'' b <=> el tiempo de vida a se puede usar de manera segura en un contexto donde se espera el tiempo de vida b.

El problema que tengo es que no puedo conciliar esto. ¿Cómo llegas del # 2 al # 1? Porque para mí, esto parece una contradicción ... Si esperas que algo esté vivo durante al menos b y tienes algo con un tiempo de vida que es más corto que b, obviamente no puedes usarlo en ese contexto donde algo con un tiempo de vida b se requiere, ¿puedes? ¿Soy solo yo o obtuvimos la relación de subtipo por tiempos de vida incorrectos?

Edición: (*) Según Ms2ger en el canal IRC #rust , este es el caso. También encaja con la documentación del marcador de vida útil contravariante que se utiliza en el iterador de Items .

Edit2: Se han eliminado los marcadores ContravariantLifetime y CovariantLifetime. Ahora tenemos PhantomData como reemplazo en el módulo marker .